Edited at

Bashで引数で使うワイルドカード展開を抑制する

More than 1 year has passed since last update.

シェルスクリプトで aws の s3 sync について --exclude にワイルドカードを指定するのに、うまく指定できなくて調べた結果をまとめておく。ちゃんと指定しないとクオートやエスケープの文字自体も受け渡されてしまって --exclude が意図したとおりに指定できなかった。


直接使う場合

Bashでワイルドカード展開を抑制する方法はいくつかあります。

bash - Stop shell wildcard character expansion? - Stack Overflow より

$ # quote it

$ foo '*'

$ # or escape it
$ foo \*

$ # or disable the glob (noglob)
$ set -f
$ foo *


変数で使う場合

変数にワイルドカード文字を代入すると、とたんに難しくなります。


文字列だけ

sh での変数とワイルドカードの落とし穴|てくめも@ecoop.net

のように、"$A" でダブルクオートで括れば展開されないが、空白で区切られた複数のオプションを含む場合にコマンド引数で使用しようとすると不具合が出る。空白も含めて1つの文字列として受け渡されてしまいます。


引数で使う場合

今のところ、以下の2つで解決できるようです。


  • eval を使う

  • set -f を使う


eval を使う

aws s3 syncするシェルスクリプトでワイルドカードでexcludeしたときのメモ


実験


環境

$ ll

合計 1024
-rw-r--r--+ 1 xxx Users 0 8月 24 18:27 1.txt
-rw-r--r--+ 1 xxx Users 0 8月 24 18:27 2.txt
-rw-r--r--+ 1 xxx Users 0 8月 24 18:27 3.txt
-rwxr-xr-x+ 1 xxx Users 269 8月 24 19:10 sample.sh*


sample.sh

#!/bin/bash

subject1="--exclude '*' --include '*.txt'"
subject2="--exclude * --include *.txt"
subject3='--exclude \* --include \*.txt'

set -x
#
:
echo ${subject1}
:
echo "${subject2}"
:
eval "echo ${subject1}"
:
set -f
echo ${subject2}
set +f
:
echo ${subject3}
:
#



結果

ワイルドカード文字が * だけになっていると良い。

$ ./sample.sh

+ :
+ echo --exclude ''\''*'\''' --include ''\''*.txt'\'''
--exclude '*' --include '*.txt'
+ :
+ echo '--exclude * --include *.txt'
--exclude * --include *.txt
+ :
+ eval 'echo --exclude '\''*'\'' --include '\''*.txt'\'''
++ echo --exclude '*' --include '*.txt'
--exclude * --include *.txt
+ :
+ set -f
+ echo --exclude '*' --include '*.txt'
--exclude * --include *.txt
+ set +f
+ :
+ echo --exclude '\*' --include '\*.txt'
--exclude \* --include \*.txt
+ :

1番目は、展開後なのに引数の文字列としてシングルクオートが含まれてしまっている。

2番目は、一見うまく展開されているようだけど、'--exclude * --include *.txt'と1つの文字列としてシングルクオートで括られている。

3番目は、OK。

4番目は、OK。

5番目は、エスケープ文字が含まれているので駄目。