Bash

for循環中的命令替換不起作用

  • August 27, 2013

我想保留所有文件.bat

我試過

for f in $(ls | egrep -v .bat); do echo $f; done

for f in $(eval ls | egrep -v .bat); do echo $f; done

但是這兩種方法都會產生相同的結果,因為它們會列印所有內容。而如果ls | egrep -v .bat與循環eval ls | egrep -v .bat分開使用,則本身就可以工作。for

編輯

有趣的是,如果我省略了-v標誌,循環會做它應該做的事情並列出所有以.bat.


隨意編輯問題標題,因為我不確定問題是什麼。

我正在使用GNU bash, version 4.1.10(4)-release (i686-pc-cygwin).


例子

$ ls -l | egrep -v ".bat"
total 60K
-rwx------+ 1 SYSTEM SYSTEM 5.3K Jun  6 20:31 fsc*
-rwx------+ 1 SYSTEM SYSTEM 5.3K Jun  6 20:31 scala*
-rwx------+ 1 SYSTEM SYSTEM 5.3K Jun  6 20:31 scalac*
-rwx------+ 1 SYSTEM SYSTEM 5.3K Jun  6 20:31 scaladoc*
-rwx------+ 1 SYSTEM SYSTEM 5.3K Jun  6 20:31 scalap*

命令正在工作,但不在 for 循環中。

$ for f in $(ls | egrep -v .bat); do echo $f; done
fsc
fsc.bat
scala
scala.bat
scalac
scalac.bat
scaladoc
scaladoc.bat
scalap
scalap.bat
scalac
scalac.bat
scaladoc
scaladoc.bat
scalap
scalap.bat

調試 $ set -x

mike@pc /cygdrive/c/Program Files (x86)/scala/bin
$ for f in $(ls | egrep -v .bat); do echo $f; done
++ ls -hF --color=tty
++ egrep --color=auto -v .bat
+ for f in '$(ls | egrep -v .bat)'
+ echo fsc
fsc
+ for f in '$(ls | egrep -v .bat)'
+ echo fsc.bat
fsc.bat
// and so on

您的程式碼中有一些錯誤:

使用不帶引號的命令替換 ( $(...)) 而不設置$IFS

不加引號的擴展是 split+glob 運算符。預設是按空格、製表符和換行符分割。在這裡,您只想在換行符處拆分,因此您需要將 IFS 設置為否則這意味著如果文件名包含空格或製表符,則將無法正常工作

使用不帶引號的命令替換set -f.

不加引號的擴展是 split+glob 運算符。在這裡您不想要萬用字元,即將萬用字元擴展為scala*匹配文件列表。當您不希望 shell 進行 globbing 時,您必須使用以下命令禁用它set -f

ls別名為ls -F

上述問題因您ls別名為ls -F. 它添加/到目錄和*執行檔中。因此,通常,因為scala它是可執行的、ls -F輸出scala*的,並且作為一個通配模式,它被擴展為所有以它開頭的文件名,scala這解釋了為什麼它似乎egrep -v沒有過濾掉文件。

假設文件名不包含換行符

換行符與文件名中的任何字元一樣有效。所以解析的輸出ls通常是行不通的。例如,ls在包含ab文件的目錄中的輸出與在包含一個名為a\nb.

上面egrep將過濾文件名的行,而不是文件名

使用egrep代替grep -E

egrep已棄用。grep -E是標準等價物。

不轉義.正則表達式運算符。

上面,您曾經egrep啟用擴展正則表達式,但沒有使用任何擴展的 RE 特定運算符。您使用的唯一 RE 運算符是.匹配任何字元,但看起來這不是您想要的。所以你不妨在grep -F這裡使用。或使用grep -v '\.bat'.

不在行尾錨定正則表達式

egrep .bat將匹配任何包含任何後跟字元的行bat,因此這就是正則表達式,表示任何不包含bat在第一個位置的內容。它應該是grep -v '\.bat$'

不加$f引號

不加引號的擴展是 split+glob 運算符。在那裡,你都不想要,所以$f應該引用 ( "$f")。

採用echo

echo在其參數中擴展 ANSI C 轉義序列和/或根據實現(和/或環境)像-n或特別對待字元串。-e``echo

改用printf. _

所以一個更好的解決方案:

for f in *; do
 case $f in
   (*.bat);;
   (*) printf '%s\n' "$f"
 esac
done

雖然如果目前目錄中沒有非隱藏文件,那仍然會輸出*. 您可以zsh通過更改**(N)bash執行來解決此問題shopt -s nullglob

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