$ myvar="/path to/my directory"
$ Sudo bash -c "cd $myvar"
そのような場合、どのように引用できますか$myvar
myvar
の値に空白があるためにWordの分割を回避するには?
_$myvar
_は引用符で囲まれていないため、そのコードにはWord splitting(引用符で囲まれていない展開時に変数を分割する機能のように)はありません。
ただし、_$myvar
_がbash
に渡される前に展開されるため、コマンドインジェクションの脆弱性があります。したがって、その内容はbashコードとして解釈されます!
そこにスペースがあると、いくつかの引数がcd
に渡されます。これはWord splittingのためではなく、シェル構文でいくつかのトークンとして解析されるためです。 _bye;reboot
_の値を指定すると、再起動します!¹
ここでは、次のようにします。
_Sudo bash -c 'cd -P -- "$1"' bash "$myvar"
_
(ここで、インラインスクリプトの最初の引数として_$myvar
_の内容を渡します。IFSワード分割を防ぐために、それぞれのシェルに対して_$myvar
_と_$1
_の両方が引用されていることに注意してください(そしてグロビング))。
または:
_Sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'
_
(環境変数で_$myvar
_の内容を渡す場合)。
もちろん、インラインスクリプトでonlycd
を実行しても、役立つものは何もありません(ただし、root
がcd
にアクセスできるかどうかを確認する以外)。おそらく、そのスクリプトでcd
を実行し、次のような何かを実行したいとします。
_Sudo bash -c 'cd -P -- "$1" && do-something' bash "$myvar"
_
Sudo
を使用して、他の方法ではアクセスできないディレクトリにcd
をアクセスできるようにする意図がある場合、それは実際には機能しません。
_Sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"
_
_$myvar
_の現在のディレクトリでインタラクティブなbash
を開始します。しかし、そのシェルはroot
として実行されます。
あなたがすることができます:
_Sudo sh -c 'cd -P -- "$1" && exec Sudo -u "$Sudo_USER" bash' sh "$myvar"
_
現在のディレクトリが_$myvar
_である非特権インタラクティブbash
を取得するには、最初にそのディレクトリにcd
へのアクセス許可がないと、そのディレクトリでは何もできません現在の作業ディレクトリです。
_$ myvar=/var/spool/cron/crontabs
$ Sudo sh -c 'cd -P -- "$1" && exec Sudo -u "$Sudo_USER" bash' sh "$myvar"
bash-4.4$ ls
ls: cannot open directory '.': Permission denied
_
例外は、ディレクトリ自体に対するsearch権限があるが、そのパスのディレクトリコンポーネントの1つに対する権限がない場合です。
_$ myvar=1/2
$ mkdir -p "$myvar"
$ chmod 0 1
$ cd 1/2
cd: permission denied: 1/2
$ Sudo sh -c 'cd -P -- "$1" && exec Sudo -u "$Sudo_USER" bash' sh "$myvar"
bash-4.4$ pwd
/home/stephane/1/2
bash-4.4$ mkdir 3
bash-4.4$ ls
3
bash-4.4$ cd "$PWD"
bash: cd: /home/stephane/1/2: Permission denied
_
¹厳密に言えば、$(seq 10)
(文字通り)のような_$myvar
_の値の場合、bash
シェルによるコマンド置換の展開時にWord分割が当然発生し、root
として開始されます
新しい(bash 4.4)引用マジックがあり、より直接的にやりたいことができます。より大きなコンテキストによっては、他の手法のいずれかを使用する方が良い場合がありますが、この限られたコンテキストで機能します。
Sudo bash -c "cd ${myvar@Q}; pwd"
$myvar
の内容を信頼できない場合は、GNU printf
を使用してエスケープバージョンを作成するのが妥当なアプローチです。
#!/bin/bash
myvar=$(printf '%q' "$myvar")
bash -c "echo $myvar"
printf
のmanページにあるように:
%q
ARGUMENTは、シェル入力として再利用できる形式で出力され、提案されたPOSIX
$''
構文を使用して、出力できない文字をエスケープします。
StéphaneChazelasの提案は、間違いなくこれを行うための最良の方法です。 R ..の回答のようにsed
サブプロセスを使用するのではなく、パラメーター展開構文で安全に引用する方法についての回答を提供します。
myvar='foo \'\''"bar'
quote() {
printf "'%s'" "${1//\'/\'\\\'\'}"
}
printf "value: %s\n" "$myvar"
printf "quoted: %s\n" "$(quote "$myvar")"
bash -c "printf 'interpolated in subshell script: %s\n' $(quote "$myvar")"
そのスクリプトの出力は次のとおりです。
value: foo \'"bar
quoted: 'foo \'\''"bar'
interpolated in subshell script: foo \'"bar
まず最初に、他の人がcd
コマンドはすぐに終了するシェルのコンテキストで発生するため、役に立たないことに気づきました。しかし、一般的にこの質問は理にかなっています。必要なのは、Shellで任意の文字列を引用する方法であり、Shellで引用されたものとして解釈されるコンテキストで使用できます。ストリング。私の shトリック ページにこの例があります:
quote () { printf %s\\n "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" ; }
この関数を使用すると、次のことができます。
myvar="/path to/my directory"
Sudo bash -c "cd $(quote "$myvar")"
はい、単語分割があります。
まあ、技術的には、bashで行を解析する際のコマンドライン分割。
最初に引用を正しくしましょう:
私があなたがそれを望んでいると私が思うようにコマンドを機能させるための最も簡単な(そして安全でない)解決策は次のとおりです:
bash -c "cd \"$myvar\""
何が起こるか確認してみましょう(myvar="/path to/my directory"
を想定):
$ bash -c "printf '<%s>\n' $myvar"
<./path>
<to/my>
<directory>
ご覧のとおり、パス文字列はスペースで区切られています。そして:
$ bash -c "printf '<%s>\n' \"$myvar\""
<./path to/my directory>
分割されていません。
ただし、コマンドは(書かれているとおり)安全ではありません。より良い使用:
$ bash -c "cd -P -- \"$myvar\"; pwd"
/home/user/temp/path to/my directory
これにより、ダッシュ(-)で始まるファイルまたは問題を引き起こす可能性のあるリンクをたどるファイルからcdが保護されます。
$myvar
の文字列がパスであると信頼できる場合、これは機能します。
ただし、「文字列を実行」しているため、「コードインジェクション」は引き続き可能です。
$ myvar='./path to/my directory";date;"true'
$ bash -c "printf '<%s> ' \"$myvar\"; echo"
<./path to/my directory> Tue May 8 12:25:51 UTC 2018
次のように、文字列をより強く引用する必要があります。
$ bash -c 'printf "<%s> " "$1"; echo' _ "$myvar"
<./path to/my directory";date -u;"true>
上記のように、値は解釈されず、"$1'
としてのみ使用されました。
これで(安全に)Sudoを使用できます。
$ Sudo bash -c 'cd -P -- "$1"; pwd' _ "$myvar"
_: line 0: cd: ./path to/my directory";date -u;"true: No such file or directory
引数が複数ある場合は、次のものを使用できます。
$ Sudo bash -c 'printf "<%s>" "$@"' _ "$val1" "$val2" "$val3"