쉘 변수에 저장된 경로 목록이 있습니다 tmp
. 예를 들면 다음과 같습니다.
/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S2_L001_I1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_I2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_I2.tar
경로의 일치하는 패턴을 기반으로 새 디렉터리를 만들고 싶습니다. 위의 예에서는 디렉터리를 ZRT834_9
만들고 해당 디렉터리에 대한 파일 ZRT207_1
에 대한 소프트 링크를 만들고 싶습니다 .tar
ZRT834_9
내 출력은 다음 S1_L001_R1.tar
과 같아야 합니다 S2_L001_I1.tar
.S1_L001_I2.tar
어떻게 이를 달성할 수 있나요?
답변1
존재하다 zsh
:
files=(
/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S2_L001_I1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_I2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_I2.tar
)
# or files=(${(f)"$(<list.txt)"}) to get the file list from the non-empty
# lines of list.txt, or files=($=tmp) for word splitting the contents
# of a $tmp scalar variable according to the current value of $IFS.
for file ($files) {
dir=${file:h5:t}
mkdir -p -- $dir && ln -s -- $file $dir/
}
-comComponent1의 헤더 와 결과의 꼬리를 ${file:h5}
가져올 위치입니다 . 아니면 끝부터 셀 수도 있습니다.5
h
$file
:t
${file:t3:h1}
¹ 여기요소경로 구성 요소입니다. 예를 들어 에서 구성요소 는 , , , 및 ../a//b///c/./d/e
입니다 . 주고 항복할 것이다. and를 적용하기 전에 사양에 대한 절대 경로 가져오기도 참조하세요...
a
b
c
.
d
e
:h5
../a//b///c/.
:t
.
${file:A:h5:t}
:h5
:t
답변2
귀하의 설명을 바탕으로 다음과 같은 가정이 이루어졌습니다.
- 변수
tmp
에는 줄 바꿈으로 구분된 파일 이름 목록이 포함되어 있습니다.[1] - 당신은 추출하고 싶어다섯파일 이름의 경로 요소(예:
ZRT834_9
및ZRT207_1
) - 하위 디렉터리가 아직 존재하지 않는 경우 이 경로 요소를 사용하여 하위 디렉터리를 생성하려고 합니다.
- 파일 이름을 새로 생성된 디렉터리에 심볼릭 링크하려고 합니다.
#!/bin/bash
tmp="/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S2_L001_I1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_I2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_I2.tar"
while read -r f ; do
d="$(echo "$f" | sed -E 's:^(/+[^/]+){4}/+([^/]*)/.*:\2:')"
[ -z "$d" ] && echo "Error: no fifth element in path: '$f'" && exit 1
mkdir -p "$d" || exit 1
ln -s "$f" "$d/"
done <<< "$tmp"
스크립트 sed
는 확장된 정규식(옵션)을 사용하고 (대략 영어로 번역됨) -E
(하나 이상의 슬래시 뒤에 하나 이상의 슬래시가 아닌 문자가 오는)의 처음 4개 그룹( )을 캡처 그룹 1로 캡처한 다음 하나를 캡처합니다. 하나 이상의 슬래시는 캡처 그룹 2로 들어가고 전체 입력 줄을 캡처 그룹 2( )로 바꿉니다.{4}
/+[^/]+
[^/]+
\2
"하나 이상의 슬래시"는 이와 같은 경로 이름이 완벽하게 유효하기 때문입니다 /foo/////////////////bar////baz
. 추가 /s는 무시됩니다. 그런데, 일부 프로그램( smbclient
예:시작서버 이름 앞에 슬래시 2개를 사용하지만 대부분의 프로그램에서는 그렇지 않습니다.
[1] 이를 위해서는 배열을 사용해야 합니다. 예를 들어
#!/bin/bash
# double-quote each array element even though your sample
# data doesn't need to be quoted - because other filenames
# might contain white-space or shell metacharacters.
tmp=("/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_R1.tar"
"/abc/bcd/def/ZRT834/ZRT834_9/5678/S2_L001_I1.tar"
"/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_I2.tar"
"/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R1.tar"
"/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R2.tar"
"/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_I2.tar")
for f in "${tmp[@]}" ; do
d="$(echo "$f" | sed -E 's:^(/+[^/]+){4}/+([^/]*)/.*:\2:')"
[ -z "$d" ] && echo "Error: no fifth element in path: '$f'" && exit 1
mkdir -p "$d" || exit 1
ln -s "$f" "$d/"
done
답변3
GNU sed는 대체 구조의 backlink 매개변수를 사용하여 쉘 명령을 실행할 수 있습니다:
sed 's%.*/\([^/]*/\)[^/]*/[^/]*%mkdir -p "\1";ln -s "&" "\1"%e;d' <<<"$tmp"