Bash

處理單個文件作為整個管道的輸入和輸出

  • February 17, 2016

晚上好,

我想用一些管道命令過濾文件的內容,然後將結果寫回同一個文件。我知道,我不能按照我寫的方式那樣做。堅持,稍等 …

這是我擁有的一段 bash 腳本。

grep '^[a-zA-Z.:]' "$filepath" \
   | sed -r '/^(rm|cd)/d' \
   | uniq -u \
   > "$filepath"

所以我認為我可以成功,而是使用流程替換。然後我寫道:

grep '^[a-zA-Z.:]' < <(cat "$filepath") | …

這也沒有解決任何問題。我希望程序替換能夠在某處“保存”我的輸入文件內容,例如在臨時文件中。它接縫我也不了解過程替換。

我閱讀了有關“就地”版本的主題,但這些文章強調了一些二進製文件的特殊選項,例如sed -iorsort -o但我需要一個通用解決方案(我的意思是它必須適合任何管道命令)。

那麼首先,為什麼“管道標準方式”不能做到這一點,下面發生了什麼?:/我應該如何解決我的問題?有人可以解釋一下這是怎麼一回事嗎?

謝謝你。

如前所述,moreutils的海綿很棒。我使用這個腳本來模擬以避免 moreutils 依賴:

#!/bin/sh -e
#Soak up input and tee it to arguments
st=0; tmpf=
tmpf="`mktemp`" && exec 3<>"$tmpf" || st="$?"
rm -f "$tmpf" #remove it even if exec failed; noop if mktemp failed
[ "$st" = 0 ] || exit "$st"
cat >&3
</dev/fd/3 tee "$@" >/dev/null

你可以像這樣使用它:

grep '^[a-zA-Z.:]' "$filepath" \
| sed -r '/^(rm|cd)/d' \
| uniq -u | sponge "$filepath" 

您不能使用簡單的輸出重定向來執行此操作,因為重定向發生在命令啟動之前,並且輸出重定向會截斷輸出文件。

換句話說,當 grep(管道的第一個簡單命令)啟動時,最後一次重定向已經截斷了輸入/輸出文件。

據我所知,實際上並沒有任何標準的 UNIX 實用程序可以進行真正的就地編輯。sed -i僅使用臨時文件模擬它。我猜原因是如果管道步驟失敗,真正的就地過濾很容易損壞文件。

至於下面發生的事情——兩者都|使用<()系統管道,它們一次通過 IO 一個緩衝區。該機制不會創建臨時文件(無論如何都不是真實的(文件系統)文件),它會嘗試避免一次將整個輸入保存在記憶體中。

引用自:https://unix.stackexchange.com/questions/263724