93120


"アイス" という文字列をファイル名に含むファイルを "アイス" フォルダに移動させるシェルスクリプトです。  
コード中にアイスが２つあります。これを１箇所だけにしたいですが、書き方がわかりません。  
教えていただけると嬉しいです。

[code]

    search_name='アイス'
    mkdir $search_name
    names=`find . -type f -name '*アイス*' -maxdepth 1 | sed 's/.\///'`
    echo $names | while read file_name
    do
        echo $file_name
        mv $file_name $search_name/
    done
    
[/code]

ダメだったコード

[code]

    names=`find . -type f -name *$search_name* -maxdepth 1 | sed 's/.\///'`
    names=`find . -type f -name *${search_name}* -maxdepth 1 | sed 's/.\///'`
    names=`find . -type f -name '*${search_name}*' -maxdepth 1 | sed 's/.\///'`
    
[/code]


CC BY-SA 4.0
2022-12-18T04:14:41.947
92859
2022-12-31T10:29:02.900
2022-12-31T10:29:02.900
19110
51289
question
bash shell
バッククォートの中で変数展開ができない


`bash`の`man`をよく読みましょう。

### パス名展開

質問の1,2番目のケースではクォートがないので、[パス名展開](http://www.linuxcertif.com/man/1/bash/ja/#%E3%83%91%E3%82%B9%E5%90%8D%E5%B1%95%E9%96%8B_761374501h)が行われます。

> -f オプションが指定されていなければ、単語分割を行った後に bash はそれぞれの単語が *, ?, [ を含んでいるかどうか調べます。
> これらの文字のいずれかが見つかると、その単語は パターン と見なされ、 パターンにマッチするファイル名を
> アルファベット順にソートしたリストに置換されます。

例えば、以下の例のように`*`は`bash`によりパス名展開されます。

[code]

    $ mkdir tmp
    $ cd tmp
    $ touch hoge
    $ echo *
    hoge
    $ cd ..
    $ rm -r tmp
    
[/code]

このパス名展開は`find`が呼び出される前に`bash`によって行われるため、`find`の`-name`オプションが正しく指定できません。`*`という文字を展開せずに正しく`find`に渡すためにはクォートが必要になります。

#### 【補足】findに正しく渡らない例

[code]

    $ ls -1
    a1
    a2
    $ find . -name '*'
    .
    ./a1
    ./a2
    $ find . -name *
    find: paths must precede expression: `a2'
    find: possible unquoted pattern after predicate `-name'?
    $ # シングルクォートがないケースはbashでパス名展開されるため、以下と同じ
    $ find . -name a1 a2
    find: paths must precede expression: `a2'
    find: possible unquoted pattern after predicate `-name'?
    $ 
    
[/code]

## クォート

[クォート](http://www.linuxcertif.com/man/1/bash/ja/#%E3%82%AF%E3%82%A9%E3%83%BC%E3%83%88_761374487h)には

> クォート (quoting) を使うと、 特定の文字や単語のシェルに対する特別な意味を取り除けます。
> クォートを用いると、特殊文字の特殊な扱いを無効にすることや、 予約語が予約語として識別されることを防いだり、 パラメータの展開を防げます。

とあります。つまりクォートされると`*`が特殊な意味を持たないケースがあるということです。

続けて読むと

> クォートの方法には、 エスケープ文字(escape character), シングルクォート、ダブルクォートの 3 種類があります。

とあり、さらに読み続けると

> シングルクォートで文字を囲むと、 クォート内部のそれぞれの文字は文字としての値を保持します。

> ダブルクォートで文字を囲むとクォート内部の全ての文字は文字としての値を 保持しますが、 $, `, \ は例外となります。 $ と `
> はダブルクォートの内部でも特殊な意味を失いません。

ということが分かります。`*`を特殊な文字としないためにはシングルクォート(`'`)でいいのですが、これにすると`$`まで特殊な文字とされないため、[パラメータの展開](http://www.linuxcertif.com/man/1/bash/ja/#%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%AE%E5%B1%95%E9%96%8B_761374496h)が行われません。つまり、質問の3番目のケースで`'*${search_name}*'`では`search_name`のパラメータ展開が行われません。シングルクォートをダブルクォートに変えれば`search_name`のパラメータ展開が行われ、パス名展開は行われません(ダブルクォートでも`*`自体は特殊な意味を保ち続けますが、それはパラメータ展開においてだけで、パス名展開は行われません)。

## 回答

metropolisさんが質問でコメントしてくれているように以下で問題ありません。

[code]

    $ find . -type f -name "*${search_name}*"
    
[/code]

こんなのでもOK

[code]

    $ find . -type f -name \*${search_name}\*
    
[/code]


CC BY-SA 4.0
2022-12-31T08:16:37.220
93120
2022-12-31T08:42:34.740
2022-12-31T08:42:34.740
54957
54957
92859
answer