Bash를 사용하여 파일의 각 줄에서 기본 이름을 얻는 방법

Bash를 사용하여 파일의 각 줄에서 기본 이름을 얻는 방법

다음 줄이 포함된 파일이 있습니다.

파일 1

./filled/Event_repeatFILLED.svelte
./dir2/Miscellaneous_servicesFILLED.svelte
./outline/Line_weightFILLED.svelte
./two-tone/ArchitectureFILLED.svelte
...many more lines ...

기본 이름만 포함하는 파일을 만들고 싶습니다.

파일 2

Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...

현재 다음은 작동하지 않습니다.

sed 's/^.\///' file1 > file2

Shell/Bash를 사용하여 이 작업을 어떻게 수행합니까?

답변1

sed 대체 명령에서는 다른 구분 기호를 사용해야 합니다. 예를 들어:

$ sed 's|.*/||' file 
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...many more lines ...

또는:

$ perl -pe 's|.*/||' file 
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...many more lines ...

또는 다음을 사용하여 awk:

$ awk -F'/' '{print $NF}' file 
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...many more lines ...

아니면 다음과 같은 어리석은 일도 있습니다.

$ rev file | cut -d / -f 1 | rev
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...many more lines ...

또는 주석에 제안된 대로 사용할 수도 있지만 basename이는 더 복잡하고 느려집니다.

$ while IFS= read -r fileName; do basename -- "$fileName"; done < file
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte

마지막으로, 정말로 순수한 셸 솔루션을 원한다면 다음과 같이 할 수 있습니다.

$ while IFS= read -r fileName; do printf '%s\n' "${fileName##*/}"; done < file
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...many more lines ...

그러나 마지막 두 개는 사용하지 마십시오. 효율성이 가장 낮고 복잡합니다. 바라보다쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?.

답변2

xargs및 의 GNU 구현을 사용하면 basename다음을 수행할 수 있습니다.

xargs -rd '\n' -a file1 basename -a -- > file2

사용의 장점은 또는 basename와 같은 일부 특수한 경우를 올바르게 처리할 수 있다는 것입니다 .//some/dir/

동등한 작업은 수동으로 수행됩니다.

LC_ALL=C sed -e 's:^//*$:/:;t' -e 's:/*$::; s:.*/::' < file1 > file2
  • LC_ALL=C파일 경로가 로케일의 유효한 텍스트로 구성된다는 보장이 없기 때문에 필요합니다.
  • /또는 //, ///... 특별 처리 먼저
  • 후행이 /삭제되었습니다.
  • 그런 다음 맨 오른쪽에 있는 모든 항목을 제거합니다 /.

를 사용하는 경우 (for ail) 수정자(에서 빌림 ) zsh를 사용할 수도 있습니다 .:ttcsh

print -rC1 -- ${${(f)"$(<file1)"}:t} > file2

/(또는 //, ...) 의 경우 대신 빈 문자열을 반환한다는 점에서 ///GNU와 다릅니다 .basename/

그리고 perl:

perl -MFile::Basename -lpe '$_=basename$_' < file1 > file2

답변3

너무 깊이 들어가지 않고 명령에 대한 즉각적인 질문은 점이 "모든"을 의미하는 것이 무엇인지입니다.하나정규식의 "문자"입니다. 원하는 수의 문자를 일치 시켜야 합니다 .*(표준 정규 표현식에서는 가능한 한 많이).

그러니 시도해 보세요

sed 's/^.*\///' file1 > file2

다른 답변과 의견에 언급된 주의 사항이 적용됩니다.

관련 정보