次の内容を含むディレクトリがあります。
$ mkdir dir && cd "$_"
~/dir $ mkdir a1 a2 a3 a4 b1 c1 c2 1a 2a 2b 2c 2d 3a _1 _2
~/dir $ touch a_1 a_2 a_3 a_4 b_1 c_1 c_2 1_a 2_a 2_b 2_c 2_d 3_a __1 __2
~/dir $ ls
__1 1_a __2 2_a 2_b 2_c 2_d 3_a a_1 a_2 a_3 a_4 b_1 c_1 c_2
_1 1a _2 2a 2b 2c 2d 3a a1 a2 a3 a4 b1 c1 c2
さて、これらすべてのファイルとディレクトリをグループ化したいと思います。彼らの最初の手紙によるとそしてそれらを次に移します。同じ文字を持つディレクトリ。したがって、出力は次のようになります。
~/dir $ ls
_ 1 2 3 a b c
そして使用エクサ、ツリーは次のとおりです。
~/dir $ exa --tree
.
├── 1
│ ├── 1_a
│ └── 1a
├── 2
│ ├── 2_a
│ ├── 2_b
│ ├── 2_c
│ ├── 2_d
│ ├── 2a
│ ├── 2b
│ ├── 2c
│ └── 2d
├── 3
│ ├── 3_a
│ └── 3a
├── _
│ ├── _1
│ ├── _2
│ ├── __1
│ └── __2
├── a
│ ├── a1
│ ├── a2
│ ├── a3
│ ├── a4
│ ├── a_1
│ ├── a_2
│ ├── a_3
│ └── a_4
├── b
│ ├── b1
│ └── b_1
└── c
├── c1
├── c2
├── c_1
└── c_2
ワイルドカードを使用して移動できることを知っています。
~/dir $ mkdir a && mv a* a
エラーが発生します。
mkdir: cannot create directory ‘a’: File exists
しかし、それは仕事を完了します。エラーを避けるためにこれを行うことができます。
~/dir $ mkdir temp && mv a* temp && mv temp a
その後、これをforループで使用して、私が知っているすべての文字を処理できます。しかし、問題は、その最初の文字が何であるかわからないということです。私たちはたくさんの文字を持っています。文字を知らなくてもこれを達成する方法はありますか?
答え1
すべてを繰り返します。
#!/bin/bash
for f in *; do
firstChar="${f:0:1}";
mkdir -p -- "$firstChar";
mv -- "$f" "$firstChar";
done
ディレクトリがすでに存在し、Ksh / Bash / Zsh構文「最初の文字のインポート」が使用されている場合は、文句を言わない-p
でください。mkdir
${f:0:1}
動作中です。
$ mkdir a1 a2 a3 a4 b1 c1 c2 1a 2a 2b 2c 2d 3a _1 _2
$ touch a_1 a_2 a_3 a_4 b_1 c_1 c_2 1_a 2_a 2_b 2_c 2_d 3_a __1 __2
$ ls -F
__1 _1/ 1_a 1a/ __2 _2/ 2_a 2a/ 2_b 2b/ 2_c 2c/ 2_d 2d/ 3_a 3a/ a_1 a1/ a_2 a2/ a_3 a3/ a_4 a4/ b_1 b1/ c_1 c1/ c_2 c2/
$ for f in *; do
firstChar="${f:0:1}";
mkdir -p "$firstChar";
mv -- "$f" "$firstChar";
done
$ tree
.
├── _
│ ├── __1
│ ├── _1
│ ├── __2
│ └── _2
├── 1
│ ├── 1_a
│ └── 1a
├── 2
│ ├── 2_a
│ ├── 2a
│ ├── 2_b
│ ├── 2b
│ ├── 2_c
│ ├── 2c
│ ├── 2_d
│ └── 2d
├── 3
│ ├── 3_a
│ └── 3a
├── a
│ ├── a_1
│ ├── a1
│ ├── a_2
│ ├── a2
│ ├── a_3
│ ├── a3
│ ├── a_4
│ └── a4
├── b
│ ├── b_1
│ └── b1
└── c
├── c_1
├── c1
├── c_2
└── c2
22 directories, 15 files
既存のファイルまたはディレクトリ名に文字が1つしかない場合、エラーが発生します。たとえば、という名前のファイルを作成してa
上記の方法を試すと、次のような結果が得られます。
mkdir: cannot create directory ‘a’: File exists
mv: 'a' and 'a' are the same file
これが問題の場合は、一時ファイルの名前変更、ディレクトリの作成、ファイルの移動などの複雑な作業を実行できます。
for f in *; do
firstChar="${f:0:1}";
## if the first character exists as a dir, move the file there
if [[ -d "$firstChar" ]]; then
mv -- "$f" "$firstChar";
## if it exists but is not a dir, move to a temp location,
## re-create it as a dir, and move back from temp
elif [[ -e "$firstChar" ]]; then
tmp=$(mktemp ./tmp.XXXXX)
mv -- "$firstChar" "$tmp"
mkdir -p "$firstChar"
mv -- "$tmp" "$firstChar"/"$f"
else
mkdir -p "$firstChar";
mv -- "$f" "$firstChar";
fi
done
答え2
POSIX的に:
find . -path './*' -prune -exec sh -c '
j="${1%${1#???}}"; mkdir "$j" && mv -- "$1" "$j/"
' _ {} \; 2>/dev/null
答え3
言及されていない別の方法は、ファイルとディレクトリの一意の最初の文字の両方を見つけて、すべてを一時ディレクトリに移動し、最初の文字ディレクトリを作成してからすべてを再度移動することです。
letters=($(ls -1 | sort -n | cut -c 1 | uniq))
temp=$(mktemp -dp .)
for letter in "${letters[@]}"; do
mv "$letter"* "$temp";
mkdir "$letter";
mv "$temp/"* "$letter";
done
rm -r "$temp"
このように、単一文字のディレクトリとファイルまた処理されました。実行中:
$ mkdir a1 a2 a3 a4 b1 c1 c2 1a 2a 2b 2c 2d 3a _1 _2
$ touch a_1 a_2 a_3 a_4 b_1 c_1 c_2 1_a 2_a 2_b 2_c 2_d 3_a __1 __2
$ mkdir a b c
$ touch 1 2 3 _
$ ls -F
_ 1 __2 2_a 2b/ 2_d 3_a a_1 a2/ a_4 b_1 c_1 c2/
__1 1_a _2/ 2a/ 2_c 2d/ 3a/ a1/ a_3 a4/ b1/ c1/
_1/ 1a/ 2 2_b 2c/ 3 a/ a_2 a3/ b/ c/ c_2
このスクリプト以降の結果は次のとおりです。
$ ls -F
_/ 1/ 2/ 3/ a/ b/ c/
$ exa --tree
.
├── 1
│ ├── 1
│ ├── 1_a
│ └── 1a
├── 2
│ ├── 2
│ ├── 2_a
│ ├── 2_b
│ ├── 2_c
│ ├── 2_d
│ ├── 2a
│ ├── 2b
│ ├── 2c
│ └── 2d
├── 3
│ ├── 3
│ ├── 3_a
│ └── 3a
├── _
│ ├── _
│ ├── _1
│ ├── _2
│ ├── __1
│ └── __2
├── a
│ ├── a
│ ├── a1
│ ├── a2
│ ├── a3
│ ├── a4
│ ├── a_1
│ ├── a_2
│ ├── a_3
│ └── a_4
├── b
│ ├── b
│ ├── b1
│ └── b_1
└── c
├── c
├── c1
├── c2
├── c_1
└── c_2
これにより、必要なディレクトリのみが作成されます。したがって、mkdir
コマンドはできるだけ少ない回数で実行されます。
編集:同様@terdonが言及されています、解析された出力により、その名前のファイルでls
コマンドが失敗します。newline
この問題を解決するには、次のものを使用できます。
temp=$(mktemp -dp .)
for f in *; do
letter="${f:0:1}";
mv -- "$letter"* "$temp";
mkdir "$letter";
mv "$temp/"* "$letter";
done
rm -r "$temp"
答え4
そしてzsh
:
autoload -Uz zmv # best in ~/.zshrc
() { mkdir ${(uM)@#?}; } ??* && zmv '(?)?*' '$1/$f'
私たちは:
() { body; } args
:拡張子??*
(2文字以上で構成されるファイル名)を引数として渡す匿名関数。体内:${@#?}
argsから最初の文字が削除されますが、M
引数拡張フラグ(一致に使用されます)を使用すると、状況は反転され、最初の文字のみが保持されます。u
(固有の場合)重複を削除します。- 次に、
zmv '(?)?*' '$1/$f'
少なくとも2つの文字を含むファイルをそのフォルダに移動します。