다음 내용이 포함된 디렉토리가 있습니다.
$ 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
기존 파일이나 디렉터리 이름에 문자가 하나만 있으면 오류가 발생합니다. 예를 들어, 이라는 파일을 만들고 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개의 문자가 포함된 파일을 해당 폴더로 이동합니다.