Shell

如何在 Unix shell 中使用特殊字元作為普通字元?

  • July 21, 2021

許多問題,例如“如何鍵入雙引號字元 (")?” 正在被問到,我們不想用相同的答案混淆我們的社區(鍵入它,\"如果沒有包含在's 中,"如果包含在's 中。)所以,問題就在這裡。

您不能像普通字元那樣在終端中鍵入特殊字元,例如此命令將失敗:

echo Updates (11)

那麼,如何在終端中輸入這些字元,就好像它們是普通字元一樣?

!#$^&*?[](){}<>~;'"\|<space><tab><newline>

這在很大程度上取決於外殼。查看您的 shell 手冊以獲取詳細資訊。

另請注意,某些字元僅在某些情況下是特殊的。例如,在大多數 shell 中,*並且?僅在列表上下文中是特殊的,在 POSIX 或類似 csh 的 shell 中,~僅在單詞的開頭或在某些字元(如:. 相同的=in zsh。在某些 shell 中,[只有在與 a 匹配(有一些限制)時才是特殊的]

在某些 shell(如bashor yash)中,特殊字元(如空白標記分隔符)也因語言環境而異。

引用運算符(以刪除這些字元的特殊含義)在 shell 之間也有很大差異。

伯恩狀貝殼

Bourne-like shell 的摘要(即sh自 80 年代以來已知在某個系統或另一個系統上呼叫的 shell):

伯恩貝殼

特殊的角色:

  • "'&|;()^<>$`、空格、換行符和製表符在不加引號的簡單命令行中是特殊的。
  • #(早期版本除外)在行首或未加引號的空格、製表符或`&|()^<>;``.
  • {並且}唯一的特殊之處在於它們是 shell 關鍵字(因此只有命令位置的單詞)。
  • *?[作為萬用字元是特殊的,所以只在列表上下文中。在 的情況下[,它[...]是萬用字元,要麼[]只需要被引用以消除特殊含義。
  • =在將其視為賦值運算符的上下文中是特殊的。也就是說,在一個簡單的命令中,對於所有不跟隨參數的單詞(除了 after set -k)。

報價運算符

  • \引用除換行符以外的所有特殊字元(\&lt;newline&gt;是一種將長邏輯行繼續到下一個物理行的方法,以便刪除該序列)。請注意,反引號在其中增加了額外的複雜性,\首先用於轉義結束反​​引號並幫助解析器。在雙引號內,\只能用於轉義自身,"$(```仍然\&lt;newline&gt;是續行)。在此處文件中,除了". \是在此處文件中轉義字元的唯一方法。
  • "..."雙引號轉義除自身之外的所有字元,\,$和```.
  • '...'單引號轉義除自身之外的所有字元。

POSIX 外殼

POSIX shell 的行為與 Bourne shell 非常相似,除了:

  • ^不再是特殊字元
  • ~在某些情況下很特別
  • {允許是特殊的,所以應該引用。

ksh

像 POSIX 一樣,除了:

  • {string}如果字元串包含未引用的,(或..在某些情況下和某些版本中),則它是特殊的。
  • ksh93 有一個額外的特殊引用運算符:$'...'具有復雜的規則。bash該運算符也可以在、zshmkshFreeBSD 和 busybox中找到(有一些變化)sh
  • ksh93還有一個$"..."引用運算符,"..."除了字元串需要本地化(可以配置為翻譯成使用者的語言)外,它的工作方式與此類似。mksh忽略$in $"..."
  • 因為ksh93r,在互動式 shell 中ksh93支持 csh 樣式的歷史擴展(預設情況下未啟用),-H-o histexpand使得^在命令的開頭和!特殊。!然後在某些上下文中是特殊的(不是在後面跟著空格或 TAB 時,也不是在此處的文件中),並且不會被雙引號轉義。只有反斜杠(不在雙引號內)和單引號轉義它。

bash

ksh93但是:

  • 在單字節字元語言環境中,所有空白(根據語言環境)字元都被視為分隔符(如空格或製表符)。實際上,這意味著您應該引用設置了第 8 位的所有字節,以防它們在某些語言環境中可能是空白字元。
  • 在互動式實例中預設啟用 csh 歷史擴展,其註釋與上述 ksh93 相同,只是在較新版本的bash,!有時也不特殊,當後跟 a 時"

zsh

ksh93但是:

  • bash與csh 歷史擴展相同的註釋
  • =作為單詞的第一個字元是特殊的(=ls擴展為/bin/ls)。
  • {並且}還可以在沒有分隔的情況下打開和關閉命令組({echo text}如 Bourne 之類的作品{ echo text;})。
  • 除了[單獨之外,[即使沒有以 . 結尾,也需要引用]
  • extendedglob啟用該選項後#^~是萬用字元。
  • braceccl選項,{non-empty-string}很特別。
  • $"..."不支持。
  • 作為一個特殊的怪癖,在單詞開頭?跟隨(甚至引用或擴展)時並不特殊(以允許工作規範)%``%?name
  • 一個rcquotes選項(預設情況下未啟用)允許''在單引號 à la中輸入單引號rc(見下文)。

yash

POSIX除此之外。

  • 所有空白字元都被視為分隔符。
  • 使用該brace-expand選項,實現 zsh 樣式的大括號擴展。

對於所有 shell,在某些特殊情況下,引用的工作方式不同。我們已經在這裡提到了文件和反引號,但[[...]]在 ksh 和其他一些 shell、POSIX $((...))case構造中也有……

另請注意,在擴展(使用雙引號)或應用於此處的文件分隔符時,引用可能會產生其他副作用。它還禁用保留字並影響別名擴展。

概括

在 Bourne-like shell 中!#$^&*?[(){}&lt;&gt;~;'"|=`,、SPC、TAB、NEWLINE 和一些設置了第 8 位的字節是或可能是特殊的(至少在某些情況下)。

要刪除特殊含義以便按字面意思對待它們,請使用引用。

採用:

  • '...'刪除每個字元的特殊含義:
printf '%s\n' '\/\/ Those $quoted$ strings are passed literally as
single arguments (without the enclosing quotes) to `printf`'
  • \僅刪除一個字元的特殊含義:
printf '&lt;%s&gt;\n' foo bar\ baz #comment

上面,只有 a 前面的空格字元按\字面意思傳遞給printf. 其他的被 shell 視為標記分隔符。

  • 用於"..."引用字元,同時仍然允許參數擴展($var, $#, ${foo#bar}…),算術擴展($((1+1)),也在$[1+1]某些 shell 中)和命令替換$(...)或舊形式...。實際上,大多數時候,您確實希望將這些擴展放在裡面任何情況下的雙引號。您可以使用\within"..."刪除仍然特殊的字元的特殊含義(但僅限於它們)。
  • 如果字元串包含'字元,您仍然可以使用其餘部分並使用其他'...'可以引用或或(如果可用)的引用機制:'``"'"``\'``$'\''
echo 'This is "tricky", isn'\''t it?'
  • 使用現代$(...)形式的命令替換。僅使用舊...的以與 Bourne shell 兼容,即非常舊的系統,並且僅在變數分配中使用,如不要使用:
echo "`echo "foo bar"`"

這不適用於 Bourne shell 或 AT&T 版本的 ksh。或者:

echo "`echo \"foo bar\"`"

這將適用於 Bourne 和 AT&T ksh,但不適用於yash(**2020 年編輯:**僅在 2.41 及更早版本中,但已在 2.42 /錯誤報告/送出中更改),但使用:

var=`echo "foo bar"`; echo "$var"

這將適用於所有人。

用雙引號將它們可移植地嵌套也是不可能的,所以再次使用變數。還要注意特殊的反斜杠處理:

var=`printf '%s\n' '\\'`

將僅在 內儲存一個反斜杠$var,因為在反引號內有額外級別的反斜杠處理(對於\$(以及"在引用時除外yash`)),因此您需要

var=`printf '%s\n' '\\\\'`

或者

var=`printf '%s\n' '\\\'

反而。

Csh家族

csh 和 tcsh 具有明顯不同的語法,儘管它們與 Bourne shell 仍有許多共同點,因為它們具有共同的傳統。

特殊的角色:

  • "'&|;()^<>$`,空格,換行符和製表符在沒有被引用時在任何地方都是特殊的。
  • #(csh 是作為註釋前導引入的 shell #)在腳本開頭或未加引號的空格、製表符或換行符之後是特殊的。
  • *?[在列表上下文中作為萬用字元是特殊的
  • {non-empty-string}是特殊的(csh 是引入大括號擴展的外殼)。
  • !並且^作為歷史擴展的一部分是特殊的(再次,csh 發明),並且引用規則是特殊的。
  • ~(波浪號擴展也是 csh 的一項發明)在某些情況下是特殊的。

報價運算符

它們與 Bourne shell 相同,但行為不同。從語法的角度來看,tcsh 的行為類似於 csh,您會發現許多版本的 csh 都有嚴重的錯誤。獲取最新版本的 tcsh 以獲得大致工作的 csh 版本。

  • \轉義除換行符以外的單個字元(與 Bourne shell 相同)。它是唯一可以轉義的引用運算符!\&lt;newline&gt;不會對其進行轉義,而是將其從命令分隔符轉換為標記分隔符(如空格)
  • "..."轉義除自身$、、、換行符和`!`.之外的所有字元 與 Bourne shell 不同,您不能使用`\`to escape`$`和inside "...",但您可以使用\to escape!或 newline (但不能使用本身,除非在 a!或 newline 之前)。一個字面!量是"\!"和一個字面\!量是"\\!"
  • '...' 轉義除自身!和換行符以外的所有字元。就像雙引號一樣,!換行符可以用反斜杠轉義。
  • 命令替換僅通過...語法進行,幾乎不能可靠地使用。
  • 變數替換的設計也很糟糕並且容易出錯。運算符$var:q有助於編寫更可靠的涉及變數的程式碼。

概括

如果可以,請遠離 csh。如果你不能使用:

  • 單引號引用大多數字元。!並且換行符仍然需要一個\.
  • \可以轉義大多數字元
  • "..."可以允許在其中進行一些擴展,但是如果它們嵌入換行符和/或反斜杠字元,那將是非常錯誤的,最好僅使用單引號並$var:q用於變數擴展。如果要可靠地連接數組的元素,則需要使用循環。

rc家庭

rcplan9shell 和它的後代一樣es,並akanga已被移植到 Unix 和類 unix。這是一個語法更清晰、更好的 shell,如果我們不為向後兼容而堅持使用類似 Bourne 的 shell,每個人都會使用它。

rc/akanga

特殊的角色

  • #;&|^$=’{}()<>`, SPC, TAB 和 NEWLINE 在不被引用時總是特殊的。
  • *?[是萬用字元運算符。

報價運算符

'...'是唯一的引用運算符。文字'''單引號括起來,如下所示:

echo 'it''s so simple isn''t it?'

es

es可以看作是一個基於rc.

不過,它有一些不同之處。此 Q/A 的一個有趣之處在於\它也是一個引用運算符(引用除換行符以外的所有特殊字元),也可用於引入轉義序列,如\n換行符、\b反斜杠……

fish 是一個相對較新的人(大約 2005 年),主要用於互動式使用,並且與其他 shell 的語法也有很大不同。

特殊的角色

  • "'\()$%{}^&lt;&gt;;&|未引用時總是特別的(注意%(用於 pid 擴展)作為與其他 shell 的顯著區別,```並不特別)
  • #(註釋)在未加引號的空格、製表符、換行符或;&|^&lt;&gt;
  • *?(但不是[...])萬用字元運算符

報價運算符

  • \引用除換行符以外的單個特殊字元,但要注意它還兼作 C 轉義序列 ( \n, \b…) 介紹器。IOW,\n不是引用n而是換行符。
  • "..."引用除自身之外的所有內容,$反斜杠和反斜杠可用於轉義這些。\&lt;newline&gt;是 . 內部的續行(刪除)"..."
  • '...'引用除自身和之外的所有內容\,您可以使用反斜杠來轉義這些內容。

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