Bash

為什麼 cut 會因 bash 而不是 zsh 而失敗?

  • January 4, 2021

我創建了一個帶有製表符分隔欄位的文件。

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並且mkshyash支持<<<不做。

當涉及到數組時mkshyashzshjoin 的第一個字元和$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/yashmksh(至少版本 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)。

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