Следующее документировано вman btrfs-subvolume
:
For every subvolume the following information is shown by default: ID, gen, top level, and path where:
ID is subvolume’s id,
gen is an internal counter which is updated every transaction
top level is the same as parent subvolume’s id
and path is the relative path of the subvolume to the top level subvolume.
Рассмотрим эти примеры из вашего вопроса:
ID 256 gen 479 parent 5 top level 5 path @
ID 258 gen 479 parent 256 top level 256 path var
Подтом @ имеет родителя 5, потому что это идентификатор подтома верхнего -уровня (, не показанный в списке ). Subvolume var имеет родителя 256, потому что он находится внутри @ subvolume. Да, родитель и «верхний уровень» должны быть одинаковыми.
Просто переберите все вещи:
#!/bin/bash
for f in *; do
firstChar="${f:0:1}";
mkdir -p -- "$firstChar";
mv -- "$f" "$firstChar";
done
-p
говорит mkdir
не жаловаться, если каталог уже существует, а ${f:0:1}
является синтаксисом Ksh/Bash/Zsh для «получить первый символ».
И вот он в действии:
$ 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
Учтите, что если существующие имена файлов или каталогов состоят только из одного символа, это вызовет ошибки. Например, если вы создадите файл с именем 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
Во-первых, решение для вашего шаблона перемещения простое, просто добавьте ?
. Это соответствует только a
, за которым следует как минимум еще один символ.
mv a?* a
У нас не так много букв, но чтобы найти все реально присутствующие буквы, вы можете использовать эту команду. sed
удаляет все после первой буквы, sort uniq удаляет дубликаты.
ls | sed -r 's/^(.).*$/\1/' | sort -u
POSIXly:
find. -path './*' -prune -exec sh -c '
j="${1%${1#???}}"; mkdir "$j" && mv -- "$1" "$j/"
' _ {} \; 2>/dev/null
Другой подход, который не был упомянут, состоит в том, чтобы найти все уникальные первые буквы файлов и каталогов, переместить все во временный каталог, создать каталог с первой буквой, а затем вернуть все обратно.
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"