Bash
為什麼 cut 會因 bash 而不是 zsh 而失敗?
我創建了一個帶有製表符分隔欄位的文件。
echo foo$'\t'bar$'\t'baz$'\n'foo$'\t'bar$'\t'baz > input
我有以下腳本命名
zsh.sh
#!/usr/bin/env zsh while read line; do <<<$line cut -f 2 done < "$1"
我測試它。
$ ./zsh.sh input bar bar
這工作正常。但是,當我將第一行改為呼叫時
bash
,它會失敗。$ ./bash.sh input foo bar baz foo bar baz
為什麼這會失敗
bash
並與之合作zsh
?其他故障排除
- 在 shebang 中使用直接路徑而不是
env
產生相同的行為。- 使用管道
echo
而不是使用 here-string<<<$line
也會產生相同的行為。即echo $line | cut -f 2
。- 使用
awk
而不是cut
適用於兩個外殼。即<<<$line awk '{print $2}'
。
發生的是
bash
用空格替換製表符。"$line"
您可以通過說而不是通過顯式切割空格來避免此問題。
那是因為在4.4 之前的版本中進行了分詞,(儘管不是萬用字元)在
<<< $line
沒有引用時打開,然後將生成的單詞與空格字元連接起來(並將其放在一個臨時文件中,後跟一個換行符並使其成為標準輸入) 。bash``$line``cut
$ a=a,b,,c bash-4.3 -c 'IFS=","; sed -n l <<< $a' a b c$
tab
恰好在預設值中$IFS
:$ a=$'a\tb' bash-4.3 -c 'sed -n l <<< $a' a b$
解決方案
bash
是引用變數。$ a=$'a\tb' bash -c 'sed -n l <<< "$a"' a\tb$
請注意,它是唯一執行此操作的 shell。
zsh
(<<<
從哪裡來,靈感來自 Byron Rakitzis 的實現rc
),,ksh93
並且mksh
也yash
支持<<<
不做。當涉及到數組時
mksh
,yash
和zsh
join 的第一個字元和$IFS
空格。bash``ksh93
$ mksh -c 'a=(1 2); IFS=:; sed -n l <<< "${a[@]}"' 1:2$ $ yash -c 'a=(1 2); IFS=:; sed -n l <<< "${a[@]}"' 1:2$ $ ksh -c 'a=(1 2); IFS=:; sed -n l <<< "${a[@]}"' 1 2$ $ zsh -c 'a=(1 2); IFS=:; sed -n l <<< "${a[@]}"' 1:2$ $ bash -c 'a=(1 2); IFS=:; sed -n l <<< "${a[@]}"' 1 2$
為空時
zsh
/yash
和mksh
(至少版本 R52)之間存在差異:$IFS
$ mksh -c 'a=(1 2); IFS=; sed -n l <<< "${a[@]}"' 1 2$ $ zsh -c 'a=(1 2); IFS=; sed -n l <<< "${a[@]}"' 12$
使用時,shell 之間的行為更加一致
"${a[*]}"
(除了在為空mksh
時仍然存在錯誤)。$IFS
在
echo $line | ...
中,這是所有類似 Bourne 的 shell 中常用的 split+glob 運算符,但是zsh
(以及與 相關的常見問題echo
)。