たとえば、あなたは次のようなbash alias
があるとしましょう。
alias rxvt='urxvt'
これはうまくいきます。
しかしながら:
alias rxvt='urxvt -fg '#111111' -bg '#111111''
うまくいかないし、どちらもしない:
alias rxvt='urxvt -fg \'#111111\' -bg \'#111111\''
それでは、引用符をエスケープした後で、どのようにして文字列内の開始引用符と終了引用符を一致させることになるのでしょうか。
alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''
あなたがそのようにそれらを連結することを許可されている場合、それは同じ文字列を表すでしょうが、それは不自然に見えます。
一番外側の層で一重引用符を本当に使用したい場合は、両方の種類の引用符を接着することができます。例:
alias rxvt='urxvt -fg '"'"'#111111'"'"' -bg '"'"'#111111'"'"
# ^^^^^ ^^^^^ ^^^^^ ^^^^
# 12345 12345 12345 1234
'"'"'
が単なる'
として解釈される方法の説明:
'
一重引用符を使用する最初の引用符を終了します。"
二重引用符を使用して2番目の引用符を開始します。'
引用符付きの文字。"
二重引用符を使用して2番目の引用符を終了します。'
一重引用符を使用して3番目の引用符を開始します。(1)と(2)の間、または(4)と(5)の間に空白を入れないと、シェルはその文字列を長い1ワードとして解釈します。
'\''
:私はいつもちょうどシーケンスで各埋め込み単一引用符を置き換える(つまり:引用バックスラッシュ引用引用)文字列を閉じるには、エスケープ単一引用符を追加し、文字列を再オープンします。
私は自分のためにこれを行うために私のPerlスクリプトの中に "quotify"関数を用意しています。手順は次のとおりです。
s/'/'\\''/g # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.
これはほとんどすべてのケースを処理します。
Shellスクリプトにeval
を導入すると、人生はもっと楽しくなります。あなたは本質的に再びすべてを再引用する必要があります!
たとえば、上記のステートメントを含むquotifyというPerlスクリプトを作成します。
#!/usr/bin/Perl -pl
s/'/'\\''/g;
$_ = qq['$_'];
それを使って正しく引用符で囲まれた文字列を生成します。
$ quotify
urxvt -fg '#111111' -bg '#111111'
結果:
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
これをコピーしてaliasコマンドに貼り付けることができます。
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
(コマンドを評価に挿入する必要がある場合は、もう一度引用を実行します。
$ quotify
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
結果:
'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''
これをコピーしてevalに貼り付けることができます。
eval 'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''
Bash 2.04 構文$'string'
(単なる'string'
の代わりに;警告:$('string')
と混同しないでください)は、 ANSI Cのようなエスケープシーケンス を許し、一重引用符への拡張を可能にする別の引用メカニズムです。バージョン。
簡単な例:
$> echo $'aa\'bb'
aa'bb
$> alias myvar=$'aa\'bb'
$> alias myvar
alias myvar='aa'\''bb'
あなたの場合:
$> alias rxvt=$'urxvt -fg \'#111111\' -bg \'#111111\''
$> alias rxvt
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
一般的なエスケープシーケンスは期待通りに動作します。
\' single quote
\" double quote
\\ backslash
\n new line
\t horizontal tab
\r carriage return
以下はman bash
(バージョン4.4)から関連文書をコピーして貼り付けたものです。
$ 'string'形式の単語は特別に扱われます。 Wordは文字列に拡張され、バックスラッシュでエスケープされた文字はANSI C規格の指定に従って置き換えられます。バックスラッシュエスケープシーケンスが存在する場合は、次のようにデコードされます。
\a alert (bell)
\b backspace
\e
\E an escape character
\f form feed
\n new line
\r carriage return
\t horizontal tab
\v vertical tab
\\ backslash
\' single quote
\" double quote
\? question mark
\nnn the eight-bit character whose value is the octal
value nnn (one to three digits)
\xHH the eight-bit character whose value is the hexadecimal
value HH (one or two hex digits)
\uHHHH the Unicode (ISO/IEC 10646) character whose value is
the hexadecimal value HHHH (one to four hex digits)
\UHHHHHHHH the Unicode (ISO/IEC 10646) character whose value
is the hexadecimal value HHHHHHHH (one to eight
hex digits)
\cx a control-x character
拡大された結果は、ドル記号が存在しなかったかのように一重引用符で囲まれます。
詳細はbash-hackers.org wikiの 引用とエスケープ:ANSI Cのような文字列 を参照してください。また、 "Bash Changes" file( 概要はこちら )が、$'string'
クォーティングメカニズムに関する変更やバグ修正について多くのことを述べています。
Unix.stackexchange.comによれば どのようにして通常のものとして特殊文字を使用するのですか? それはbash、zsh、mksh、ksh93、FreeBSDおよびbusybox shで(いくらかのバリエーションを伴って)動作するはずです。
私は彼のブログ(link pls?)にエントリを見ませんが、 GNUリファレンスマニュアル によると:
文字を一重引用符( "")で囲むと、引用符内の各文字のリテラル値が保持されます。バックスラッシュが前にあっても、単一引用符は単一引用符の間には現れないかもしれません。
だからbashは理解できません:
alias x='y \'z '
ただし、二重引用符で囲むとこれが可能になります。
alias x="echo \'y "
> x
> 'y
一重引用符で囲まれた文字列内の一重引用符に'\''
を使用してもBashで機能することを確認できます。これは、スレッドの前半の "gluing"引数と同じ方法で説明できます。引用符で囲まれた文字列があるとします。'A '\''B'\'' C'
(ここでのすべての引用符は単一引用符です)。 echoに渡されると、次のように表示されます。A 'B' C
。それぞれの'\''
では、最初の引用符は現在の一重引用符で囲まれた文字列を閉じ、次の\'
は前の文字列に一重引用符を結び付けます(\'
は引用符付き文字列を開始せずに単一引用符を指定する方法です)。引用符で囲まれた文字列.
シェルで引用符をエスケープする簡単な例:
$ echo 'abc'\''abc'
abc'abc
$ echo "abc"\""abc"
abc"abc
これは、既に開いているもの('
)を終了し、エスケープしたもの(\'
)を配置してから別のもの('
)を開くことによって行われます。この構文はすべてのコマンドで機能します。それは最初の答えと非常によく似たアプローチです。
どちらのバージョンも、エスケープされた単一引用符(\ ')を使用した連結、または単一引用符を二重引用符で囲んだ連結( "'")のいずれかで機能します。
質問の作成者は、最後のエスケープ試行の最後に余分な一重引用符( ')があることに気付きませんでした。
alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''
│ │┊┊| │┊┊│ │┊┊│ │┊┊│
└─STRING──┘┊┊└─STRIN─┘┊┊└─STR─┘┊┊└─STRIN─┘┊┊│
┊┊ ┊┊ ┊┊ ┊┊│
┊┊ ┊┊ ┊┊ ┊┊│
└┴─────────┴┴───┰───┴┴─────────┴┘│
All escaped single quotes │
│
?
ASCII/Unicodeアートの前のNice部分でわかるように、最後のエスケープ単一引用符(\ ')の後に不要な単一引用符(')が続きます。 Notepad ++にあるようなシンタックスハイライトを使うことは非常に役に立ちます。
次のような別の例でも同じことが言えます。
alias rc='sed '"'"':a;N;$!ba;s/\n/, /g'"'"
alias rc='sed '\'':a;N;$!ba;s/\n/, /g'\'
これら2つのエイリアスの美しいインスタンスは、ファイルを整理する方法を非常に複雑でわかりにくくしています。つまり、多くの行があるファイルから、前の行の内容の間にコンマとスペースが入った1行だけが得られます。前のコメントを理解するために、以下は例です。
$ cat Little_Commas.TXT
201737194
201802699
201835214
$ rc Little_Commas.TXT
201737194, 201802699, 201835214
引用の問題については特に取り上げていませんが、代替アプローチを検討するのが妥当な場合もあります。
rxvt() { urxvt -fg "#${1:-000000}" -bg "#${2:-FFFFFF}"; }
これを次のように呼び出せます。
rxvt 123456 654321
これは、引用符を気にせずにこれをエイリアスできるようになるというアイデアです。
alias rxvt='rxvt 123456 654321'
あるいは、何らかの理由ですべての呼び出しに#
を含める必要がある場合は、
rxvt() { urxvt -fg "${1:-#000000}" -bg "${2:-#FFFFFF}"; }
これを次のように呼び出せます。
rxvt '#123456' '#654321'
もちろん、エイリアスは次のとおりです。
alias rxvt="rxvt '#123456' '#654321'"
(おっと、私は一種の引用を演説したと思います:)
シェルコードを使用しています。該当する場合は\x27
または\\x22
。面倒ではありません。
一重引用符で囲まれた文字列の中に一重引用符を入れることはできないので、最も簡単で読みやすいオプションはHEREDOC文字列を使用することです。
command=$(cat <<'COMMAND'
urxvt -fg '#111111' -bg '#111111'
COMMAND
)
alias rxvt=$command
上記のコードでは、HEREDOCはcat
コマンドに送信され、その出力はコマンド置換表記法$(..)
を介して変数に割り当てられます。
HEREDOCは$()
内にあるため、HEREDOCを一重引用符で囲むことが必要です。
私見の真の答えは、一重引用符で囲まれた文字列の中で一重引用符をエスケープすることはできないということです。
それは不可能だ。
Bashを使っているとしたら.
Bashマニュアルから...
Enclosing characters in single quotes preserves the literal value of each
character within the quotes. A single quote may not occur
between single quotes, even when preceded by a backslash.
他の文字列エスケープメカニズムのいずれかを使用する必要があります "または\
alias
には、一重引用符を使うことを要求する魔法はありません。
以下の両方ともbashで動作します。
alias rxvt="urxvt -fg '#111111' -bg '#111111'"
alias rxvt=urxvt\ -fg\ \'#111111\'\ -bg\ \'#111111\'
後者は、スペース文字をエスケープするために\を使用しています。
一重引用符が必要な#111111についての魔法もありません。
次のオプションは、rxvtエイリアスが期待通りに機能するという点で、他の2つのオプションと同じ結果を達成します。
alias rxvt='urxvt -fg "#111111" -bg "#111111"'
alias rxvt="urxvt -fg \"#111111\" -bg \"#111111\""
面倒な#を直接エスケープすることもできます
alias rxvt="urxvt -fg \#111111 -bg \#111111"
これらの答えの大部分は、あなたが求めている特定のケースに当てはまります。 sash、su -c
、bash -c
などのシェル拡張の複数層を通してbashコマンドを引用符で囲む必要がある場合に、友人と私が開発した一般的なアプローチがあります。任意の引用符を使用できるようにする必要があります。 、ここでネイティブbashで:
quote_args() {
local sq="'"
local dq='"'
local space=""
local arg
for arg; do
echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
space=" "
done
}
これはまさにそれが言うことをします:それは個々の引数を個別にシェルクォートします(もちろんbash展開の後):
$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'
それは1層の拡張に対して明白なことをします:
$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2
($(quote_args ...)
を囲む二重引用符は、結果をbash -c
への単一の引数にするために必要です。)さらに一般的には、複数の展開層を通して正しく引用するために使用できます。
$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2
上記の例:
quote_args
への各引数を個別にシェル引用符で囲み、結果の出力を内側の二重引用符で単一の引数に結合します。bash
、-c
、およびステップ1からのすでに一重引用符で囲まれた結果をシェル引用符で囲み、その結果を外側の二重引用符で単一の引数に結合します。bash -c
への引数として送ります。それが一言で言えばアイデアです。これでかなり複雑なことをすることができますが、評価の順序とどの部分文字列が引用されるかについて注意しなければなりません。例えば、次は間違ったことをします(「間違った」という定義のため)。
$ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
/tmp
$ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
failure
最初の例では、bashはすぐにquote_args cd /; pwd 1>&2
をquote_args cd /
とpwd 1>&2
の2つの別々のコマンドに展開するので、pwd
コマンドが実行されてもCWDは/tmp
のままです。 2番目の例は、グロビングに関する同様の問題を示しています。実際、同じ基本的な問題がすべてのbash展開で発生します。ここでの問題は、コマンド置換が関数呼び出しではないことです。文字通り1つのbashスクリプトを評価し、その出力を別のbashスクリプトの一部として使用します。
単にShell演算子をエスケープしようとすると、bash -c
に渡される結果の文字列は単なる引用符で囲まれた文字列の単なる連続であり、演算子として解釈されないため、失敗します。 bashに渡されたでしょう:
$ (cd /tmp; echo "$(quote_args cd /\; pwd 1\>\&2)")
'cd' '/;' 'pwd' '1>&2'
$ (cd /tmp; echo "$(quote_args cd /\; \[ -e \*sbin \] \&\& echo success 1\>\&2 \|\| echo failure 1\>\&2)")
'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'
ここでの問題は、あなたがオーバークォートしているということです。必要なのは、演算子を囲むbash -c
への入力として引用符で囲まないことです。つまり、演算子は$(quote_args ...)
コマンド置換の外側にある必要があります。
したがって、最も一般的な意味で実行する必要があるのは、コマンド置換時に個別に展開されることを意図していないコマンドの各Wordをシェル引用符で囲み、シェル演算子に余分な引用符を適用しないことです。
$ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
'cd' '/'; 'pwd' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
/
$ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
success
これが済んだら、文字列全体が、任意の評価レベルをさらに引用するのに公平なゲームになります。
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
/
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
/
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
/
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
success
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
success
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
success
等.
success
、sbin
、pwd
のような単語はシェルクォートで囲む必要はないことを考えれば、これらの例は見過ごされているように思われるかもしれません。ユーザーがいつRobert'; rm -rf /
を投入するのかわからないので、 は を引用符で囲む必要はありません。
カバーの下で何が起こっているのかをよりよく理解するために、2つの小さなヘルパー関数で遊ぶことができます:
debug_args() {
for (( I=1; $I <= $#; I++ )); do
echo -n "$I:<${!I}> " 1>&2
done
echo 1>&2
}
debug_args_and_run() {
debug_args "$@"
"$@"
}
それはそれを実行する前にコマンドへの各引数を列挙します:
$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
与えられた例では、外側のエスケープメカニズムとして単一引用符の代わりに単純に二重引用符を使用しました:
alias rxvt="urxvt -fg '#111111' -bg '#111111'"
このアプローチは、固定文字列をコマンドに渡すだけの場合に適しています。シェルがecho
を使用して二重引用符で囲まれた文字列を解釈する方法を確認し、必要に応じて円記号でエスケープします。
この例では、文字列を保護するには二重引用符で十分です。
$ echo "urxvt -fg '#111111' -bg '#111111'"
urxvt -fg '#111111' -bg '#111111'
これが、上で言及した「一つの真の答え」についての詳細です。
ときどき私はsshの上にrsyncを使ってダウンロードしていて、ファイル名の中に 'を2回入れてエスケープする必要があります。 (OMG!)bash用に1回、ssh用に1回。ここでは、交互の引用符区切り文字の同じ原理が機能しています。
たとえば、私たちが取得したいとしましょう:Louis TherouxのLA Stories ...
そして見よ!あなたはこれに巻きつきます:
rsync -ave ssh '"Louis Theroux"''"'"'"'"''"s LA Stories"'
これは、1つの小さな作業に対して非常に多くの作業が必要です。
入れ子になった引用符が多すぎるという問題を解決する別の方法:
小さすぎるスペースに詰め込みすぎようとしているので、bash関数を使用してください。
問題は、あなたがあまりにも多くのレベルのネストを持とうとしていることであり、そして基本的なエイリアス技術はそれに対応するのに十分強力ではありません。このようにbash関数を使用して、一重引用符と二重引用符で囲まれた引用符と渡されたパラメータがすべて通常どおり処理されるようにします。
lets_do_some_stuff() {
tmp=$1 #keep a passed in parameter.
run_your_program $@ #use all your passed parameters.
echo -e '\n-------------' #use your single quotes.
echo `date` #use your back ticks.
echo -e "\n-------------" #use your double quotes.
}
alias foobarbaz=lets_do_some_stuff
そうすれば、エイリアス関数がその整合性を壊すことを心配せずに、$ 1と$ 2の変数とシングル、ダブルクォート、バックティックを使うことができます。
このプログラムは印刷します:
el@defiant ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------
GNU Parallelがインストールされている場合、その内部引用符を使用できます。
$ parallel --shellquote
L's 12" record
<Ctrl-D>
'L'"'"'s 12" record'
$ echo 'L'"'"'s 12" record'
L's 12" record
バージョン20190222以降では、--shellquote
を複数回実行することもできます。
$ parallel --shellquote --shellquote --shellquote
L's 12" record
<Ctrl-D>
'"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
$ eval eval echo '"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
L's 12" record
サポートされるすべてのシェルで文字列を引用します(bash
だけでなく)。
この機能:
quote ()
{
local quoted=${1//\'/\'\\\'\'};
printf "'%s'" "$quoted"
}
'
の内側で'
を引用することを許可します。これを使ってください。
$ quote "urxvt -fg '#111111' -bg '#111111'"
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
二重引用符と一重引用符が混在するように、引用する行がもっと複雑になる場合、文字列を変数の中で引用符で囲むのはかなり面倒になるでしょう。そのような場合には、スクリプト内に引用符で囲む必要がある正確な行を書いてください(これに似ています)。
#!/bin/bash
quote ()
{
local quoted=${1//\'/\'\\\'\'};
printf "'%s'" "$quoted"
}
while read line; do
quote "$line"
done <<-\_lines_to_quote_
urxvt -fg '#111111' -bg '#111111'
Louis Theroux's LA Stories
'single quote phrase' "double quote phrase"
_lines_to_quote_
出力します:
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
'Louis Theroux'\''s LA Stories'
''\''single quote phrase'\'' "double quote phrase"'
一重引用符で囲まれたすべての正しく引用された文字列。
明らかに、二重引用符で囲むほうが簡単ですが、その場合の課題はどこでしょうか。これは一重引用符だけを使った答えです。私はalias
の代わりに変数を使っているので、校正のために印刷するほうが簡単ですが、alias
を使うのと同じです。
$ rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'
$ echo $rxvt
urxvt -fg '#111111' -bg '#111111'
説明
重要なのは、一重引用符を閉じて、必要なだけ何度でも開くことができるということです。例えばfoo='a''b'
はfoo='ab'
と同じです。そのため、一重引用符を閉じ、リテラルの一重引用符\'
を投入してから、次の一重引用符を再度開くことができます。
内訳図
この図では、一重引用符がどこで開閉されているかを示すために大括弧を使用して明確にしています。括弧のように引用符は "ネスト"されていません。また、正しく適用されている色の強調表示にも注意を払うことができます。引用符で囲まれた文字列はあずき色ですが、\'
は黒です。
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\' # original
[^^^^^^^^^^] ^[^^^^^^^] ^[^^^^^] ^[^^^^^^^] ^ # show open/close quotes
urxvt -fg ' #111111 ' -bg ' #111111 ' # literal characters remaining
(これは本質的にAdrianのものと同じ答えですが、私はこれがそれをよりよく説明すると感じます。また彼の答えは最後に2つの余分な単一引用符があります。)
Python 2またはPython 3内でシェル文字列を生成している場合は、引数を引用符で囲むと次のようになります。
#!/usr/bin/env python
from __future__ import print_function
try: # py3
from shlex import quote as shlex_quote
except ImportError: # py2
from pipes import quote as shlex_quote
s = """foo ain't "bad" so there!"""
print(s)
print(" ".join([shlex_quote(t) for t in s.split()]))
これは出力されます:
foo ain't "bad" so there!
foo 'ain'"'"'t' '"bad"' so 'there!'
これは別の解決策です。この関数は単一の引数を取り、上記の投票された答えが説明するのと同じように、単一引用符を使用して適切に引用します。
single_quote() {
local quoted="'"
local i=0
while [ $i -lt ${#1} ]; do
local ch="${1:i:1}"
if [[ "$ch" != "'" ]]; then
quoted="$quoted$ch"
else
local single_quotes="'"
local j=1
while [ $j -lt ${#1} ] && [[ "${1:i+j:1}" == "'" ]]; do
single_quotes="$single_quotes'"
((j++))
done
quoted="$quoted'\"$single_quotes\"'"
((i+=j-1))
fi
((i++))
done
echo "$quoted'"
}
だから、あなたはこのようにそれを使うことができます:
single_quote "1 2 '3'"
'1 2 '"'"'3'"'"''
x="this text is quoted: 'hello'"
eval "echo $(single_quote "$x")"
this text is quoted: 'hello'
Shell_escape () {
echo -nE "'${1//\'/\'\\\'\'}'"
}
実装の説明
二重引用符で囲むことで、一重引用符の折り返しを簡単に出力し、${...}
構文を使用できます。
bashの検索と置換は次のようになります。${varname//search/replacement}
'
を'\''
に置き換えています
'\''
は単一の'
を次のようにエンコードします。
'
はシングルクォーテーションを終了します
\'
は'
をエンコードします(引用符で囲まないのでバックスラッシュが必要です)
'
はシングルクォートを再開します
bashは、間にスペースを入れずに文字列を自動的に連結します。
すべての\
および\
の前に'
があります。これが${...//.../...}
のエスケープ規則だからです。
string="That's "'#@$*&^`(@#'
echo "original: $string"
echo "encoded: $(Shell_escape "$string")"
echo "expanded: $(bash -c "echo $(Shell_escape "$string")")"
P.S二重引用符で囲まれた文字列よりも単純なので、常に一重引用符で囲まれた文字列にエンコードします。