web-dev-qa-db-ja.com

解決したい変数の周りのパラメーターの拡張を防ぐ方法は?

編集:

さらに読む前の重要な注意事項: Kusalanandaのanwser なので、私が使用した別のスクリプトのバグを彼が指摘したため、私の質問はまったく役に立たなくなりました。これでこのバグが修正され、説明した以下のエラーは発生しなくなりました。

コードに2つの問題があり、$ extensionの値が展開されます。
2番目の問題は関数自体です


TL; DR

質問を書き始めたときに行き詰まりましたが、うまくいく解決策を見つけました( Shell noglob を使用)、それでも「書くことは可能でしょうか」という質問があります。

これはすでに回答済みだと思いますが、bashの拡張に関する何百ものトピックで迷子になっています。

これが私の場合です:

  • スクリプトを実行したい(たとえば、-pattern_type glob -iのような正規表現を受け入れるオプション*.JPGモードでffmpegとしましょう)
  • 引数を取る(*.JPGとしましょう)
  • 変数からの引数(たとえば$extension
  • スクリプトパラメータからの変数(たとえば$1
  • しかし、私はシェルに$extensionのようなパターンのデフォルトのように*.JPGを展開させたくありません

このすべてのアクションはスクリプトscript.shで行われることに注意してください。

したがって、ユーザーは次のコマンドを実行できます。

script.sh 'JPG' ; # where $1='JPG'

注:script.shでは、*.$1を連結して、*.JPGのようなより最終的な正規表現を取得します。

私は(ここから https://stackoverflow.com/a/11456496/912046ffmpegが引数を受け取る前にシェル拡張が発生することを読みました(ffmpegも知らないシェルの拡張が行われました)

${extension}の前後に引用符、二重引用符、または円記号をさまざまな方法で試してみましたが、失敗しました。
ここにいくつかの試みがあります:

ffmpeg ... "${extension}"

につながる:ffmpeg 1.jpg 2.jpg [...](拡張が発生します)

ffmpeg ... ${extension}

同じ

ffmpeg ... '${extension}'

検索パターン${extension}(変数名は文字通り使用されます)のため、ファイルの一致はありません

extension="\'${extension}\'" ; # wrapping the single quotes (preventing expansion) directly in variable
ffmpeg ... ${extension}

検索パターン'*.jpg'(一重引用符を含む)が原因でファイルが一致しません

ffmpeg ... \"${extension}\"

同じですが、"*.jpg"を検索しています(二重引用符を含む)

私はついにShellnoglobオプションの使用に成功しました。
好奇心旺盛な人のためのコマンドは次のとおりです。

set -f ; # disable glob (prevent expansion on '*.jpg')
ffmpeg -pattern_type glob -i ${extension} movie.mp4 ;
set +f ; # restore, but what if glob was already disabled? I should not restore it then?

しかし、私の好奇心のために、解決したい変数からのスクリプト引数の展開を防ぐ別の方法はありますか? (したがって、(一重引用符を使用して)展開を防ぐことを私が知っている方法では、変数が解決/置換されなくなります)。

次のようなもの:(ただし、安全性、注入のために文字列の連結は避ける必要があります)

ffmpeg '${extension}'
       ||           | 3
       || 2
       | 1

どこ :

  • 1:最終結果がffmpeg '*.jpg'の場合は一重引用符を開きます
  • 2:変数extension.jpgに注入して解決します
  • 3:ffmpegの単一引用符を閉じて、手動で'*.jpg'を記述したかのように引数を受け取ります。

編集:拡張変数の割り当て方法に関するコメントの後:

local extension="${2}" ;
echo $extension ;
extension="${extension:=jpg}" ; # Default on "jpg" extension
# wrapp extension type in single quotes + prefix with "*."
extension="*.${extension}" ;

次に、それを次のように使用します。

runprintcommand ffmpeg -loglevel verbose -pattern_type glob -i ${extension} "$movieName" ;

runprintcommand関数/スクリプトは次のとおりです。

function runprintcommand() {
    echo "Command to run: (Note: '\\' escape character may not be printed)" ;
    echo "$*" ;
    $* ;    
}
1
el-teedee
  • 引用符で囲まれていない変数は、変数の展開、単語分割(デフォルトではスペース、タブ、改行)を受け、生成された各単語はファイル名の生成(グロブ)を受けます。
  • 二重引用符で囲まれた変数の値は展開されますが、シェルは展開された値に対してワード分割やグロブを実行しません。
  • 一重引用符で囲まれた変数は、まったく展開されません。

例:

$ ls
script.sh
$ var='* *'
$ echo $var
script.sh script.sh
$ echo "$var"
* *
$ echo '$var'
$var

コードには2つの問題があり、$extensionの値が含まれているglobパターンに展開され、globパターンがファイル名と時期尚早に照合されます(そのままffmpeg -iに渡します。内部でのグロブ拡張)。

1つ目は、関数の呼び出しです。

runprintcommand ffmpeg -loglevel verbose -pattern_type glob -i ${extension} "$movieName" ;

ここでは、${extension}は引用符で囲まれていないため、間違いなくその値で(単語分割と)ファイル名のグロブが発生します。

2番目の問題は関数自体です。

function runprintcommand() {
    echo "Command to run: (Note: '\\' escape character may not be printed)" ;
    echo "$*" ;
    $* ;    
}

ここでは、引用符で囲まれていない$*を使用します。これにより、関数の呼び出しで${extension}を二重引用符で囲んだ場合でも、グロブが展開されます(値を単語に分割してから)。

裸の$*の代わりに、"$@"(二重引用符付き)を使用します。これは、関数の個別に引用位置パラメータに拡張されます。

これは"$@""$*"の違いです。

  • "$*"単一の二重引用符で囲まれた文字列です。通常、これを使用して引数付きのコマンドを実行することはできません。
  • "$@"二重引用符で囲まれた単語の数です。これは、引数を使用してコマンドを実行するために使用できます。
4
Kusalananda

シェルがグロブするのを防ぐ方法は、変数の展開を引用することです。 (また、Wordの分割も停止します。これは通常、必要なことでもあります)。もちろん、それはコマンド自体がグロブのようなパターンを処理するのを止めません。 ffmpeg-iに対してそれを行い、たとえばfind -name。例として後者を使用する:

$ touch a.foo b.foo c.bar
$ extension=foo
$ set -x
$ find . -name "*.$extension"
+ find . -name '*.foo'
./b.foo
./a.foo

+で始まる行はset -xから来ており、シェル実際には実行されたコマンドを示しています。)

set -fを使用してグロブを停止することもできますが、Wordの分割には役立ちません。変数に空白が含まれている場合、set -fを使用しても、引用符で囲まれていない展開は損益分岐点になります。

1
ilkkachu

これを行う別の構文を見つけたので、ここに書き留めます。

フラグを使用して無効にするシェルグロブhttp://tldp.org/LDP/abs/html/globbingref.html =)、それは拡張を防ぎます:

set -f ; # disable glob (prevent expansion on '*.jpg' to turn in "1.jpg 2.jpg 3.jpg...")
ffmpeg -pattern_type glob -i ${extension} movie.mp4 ;
set +f ; # restore
0
el-teedee