2012-06-17 122 views
2

如果要覆蓋使用bash文件,這很容易Bash,使用文件描述符覆蓋?

echo "Hello world" > hosts 

這似乎不是一個文件描述符

$ exec 3<> hosts 

$ echo "Hello world" >&3 
$ cat hosts 
Hello world 

$ echo "Hello world" >&3 
$ cat hosts 
Hello world 
Hello world 
+2

你不應該關閉並重新打開該FD的 「回聲」 調用之間,如果你想底層文件是覆蓋? – vanza

+0

什麼是你的發行版/ nix版本。適用於Cygwin 6.1 – starbolin

回答

5

這是正確的工作。打開文件的模式在shell調用open(2)時確定。當您使用FD(任何語言)時,打開文件時設置的標誌是shared between open FDs。在你的情況下,O_TRUNC只能在文件實際打開時指定。

重要的是要知道的是,只有使用<file,>file或類似方法打開文件時,才能確定模式和各種標誌。使用&修飾符複製FD基本上會創建指向原始FD的「別名」,並保留與原始狀態完全相同的狀態。截斷文件需要重新打開它。

這是我的調試功能,如果你想玩弄容易文件描述符:

lsfd() { 
    local ofd=${ofd:-2} target=${target:-$BASHPID} 

    while [[ $1 == -* ]]; do 
     if [[ -z $2 || $2 == *[![:digit:]]* ]]; then 
      cat 
      return 1 
     fi 
     case ${1##+(-)} in 
      u) 
       shift 
       ofd=$1 
       shift 
       ;; 
      t) 
       shift 
       target=$1 
       shift 
       ;; 
      h|\?|help) 
       cat 
       return 
     esac 
    done <<EOF 
USAGE: ${FUNCNAME} [-h|-?|--help] [-u <fd>] [ -t <PID> ] [<fd1> <fd2> <fd3>...] 

This is a small lsof wrapper which displays the open 
file descriptors of the current BASHPID. If no FDs are given, 
the default FDs to display are {0..20}. ofd can also be set in the 
environment. 

    -u <fd>: Use fd for output. Defaults to stderr. Overrides ofd set in the environment. 
    -t <PID>: Use PID instead of BASHPID. Overrides "target" set in the environment. 
EOF 

    IFS=, local -a 'fds=('"${*:-{0..20\}}"')' 'fds=("${fds[*]}")' 
    lsof -a -p $target -d "$fds" +f g -- >&${ofd} 
} 

我喜歡不關閉標準輸入,因爲這有時會引起問題,所以它就會先保存。

$ ({ lsfd 3; cat <&3; } {savefd}<&0 <<<'hi' 3>&0- <&"${savefd}") 
COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF  NODE NAME 
bash 920 ormaaj 3r REG  LG 0,22  3 59975426 /tmp/sh-thd-8305926351 (deleted) 
hi 

正如你可以看到,即使FD 0使用3>&0-運營商移動到3個,文件保持打開狀態O_RDONLY><的選擇對於副本描述符是任意的,只用於確定操作員左側的FD省略時的默認值。

如果你確實想打開一個新的獨立的FD,這樣的事情可能工作:

$ ({ 
cat <&4 >/dev/null; lsfd 3 4; echo there >&4; cat </dev/fd/3 
} {savefd}<&0 <<<'hi' 3>&0- 4<>/dev/fd/3 <&"${savefd}" 
) 
COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF  NODE NAME 
bash 2410 ormaaj 3r REG  LG 0,22  3 59996561 /tmp/sh-thd-8305914274 (deleted) 
bash 2410 ormaaj 4u REG  RW,LG 0,22  3 59996561 /tmp/sh-thd-8305914274 (deleted) 
hi 
there 

現在FD 4是真的「重開」到FD 3指着文件,而不僅僅是複製(即使該文件已經如上所述已經是unlink(2)'d)。首先使用cat打開第一個FD 4並查找到最後,然後再向該文件寫入一行。同時,FD3的尋找位置仍然處於開始位置,因此整個文件可以被送到終端。

相同的原則可以適用於您的情況。

$ { echo 'Hello world'; echo 'hi' >/dev/fd/1; } >hosts; cat hosts 
hi 

或者甚至可能是更好的將是剛剛打開和關閉兩個命令兩次文件,且沒有exec

我寧願避免使用exec只是爲了打開文件描述符,除非它是絕對必要的。你必須記得明確地關閉文件。如果您使用命令分組,文件將自動關閉自己,實際上給他們一個「範圍」。這在原理上類似於Python的with聲明。這可能會阻止一些混淆。

參見:

+1

+1,非常好,但* IMO *你沒有*完全*回答OP的問題。 –