web-dev-qa-db-ja.com

ドットファイルに展開されるが `..`には展開されないシェルファイル名パターン?

最近、予期せぬ方法で拡大したシェルパターンが原因で少し事故が発生しました。 /rootディレクトリにある一連のドットファイルの所有者を変更したかったので、変更しました

chown -R root .*

当然、.*..に拡張されましたが、これはちょっとした惨事でした。

bashでこの動作はシェルオプションを調整することで変更できることを知っていますが、デフォルト設定でディレクトリ内のすべてのドットファイルに展開されるが.には展開されないパターンはありますかと..

13
Ernest A

Bash、ksh、zshの方が優れたソリューションがありますが、この回答ではPOSIXシェルを想定しています。

パターン.[!.]*は、ドットで始まり、ドット以外の文字が続くすべてのファイルに一致します。 ([^.]は一部のシェルでサポートされていますが、すべてではないことに注意してください。ワイルドカードパターンの文字セット補数の移植可能な構文は[!.]です。)したがって、...は除外されます。また、2つのドットで始まるファイルもあります。パターン..?*は、2つのドットで始まり、..だけではないファイルを処理します。

chown -R root .[!.]* ..?*

これは、すべてのファイルに一致するように設定された古典的なパターンです。

* .[!.]* ..?*

このアプローチの制限は、パターンの1つが何にも一致しない場合、それがコマンドに渡されることです。スクリプトでは、...を除くディレクトリ内のすべてのファイルを照合する場合、いくつかの解決策があり、それらはすべて面倒です。

  • * .*を使用してすべてのエントリを列挙し、ループ内の...を除外します。パターンの一方または両方が何にも一致しない可能性があるため、ループは各ファイルの存在を確認する必要があります。他の基準でフィルタリングする機会があります。たとえば、ぶら下がっているシンボリックリンクをスキップする場合は、-hテストを削除します。

    for x in * .*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
    
  • コマンドが1回だけ実行される、より複雑なバリアント。位置パラメータが破壊されていることに注意してください(POSIXシェルには配列がありません)。これが問題になる場合は、これを別の関数に入れてください。

    set --
    for x in * .[!.]* ..?*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      set -- "$@" "$x"
    done
    somecommand "$@"
    
  • * .[!.]* ..?*トリプティクを使用します。この場合も、1つ以上のパターンが一致しない可能性があるため、既存のファイル(ぶら下がっているシンボリックリンクを含む)を確認する必要があります。

    for x in * .[!.]* ..?*; do
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
    
  • * .[!.]* ..?* tryptichを使用し、パターンごとに1回コマンドを実行しますが、何かに一致する場合に限ります。これにより、コマンドが1回だけ実行されます。位置パラメータが破壊されていることに注意してください(POSIXシェルには配列がありません)。これが問題になる場合は、これを別の関数に入れてください。

    set -- *
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- .[!.]* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- ..?* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    somecommand "$@"
    
  • findを使用します。 GNUまたはBSD検索では、オプション-mindepthおよび-maxdepthを使用すると、再帰を簡単に回避できます。POSIX検索では、少し注意が必要ですが、実行できます。このフォームファイルごとに1回ではなく、1回だけコマンドを簡単に実行できるという利点があります(ただし、これは保証されません。結果のコマンドが長すぎると、コマンドは複数のバッチで実行されます)。

    find . -name . -o -exec somecommand {} + -o -type d -Prune
    

これは、すべてのファイル名に少なくとも3文字(ドットを含む)が含まれている場合に機能します。

chown -R root .??*

より堅牢なソリューションとして、findを使用できます。

find . -maxdepth 1 -name '.*' -exec chown -R root {} \;
6
user26112