awk를 사용하여 파일에 대한 심볼릭 링크 목록을 이름에 공백이 있는 디렉터리로 보내시겠습니까?

awk를 사용하여 파일에 대한 심볼릭 링크 목록을 이름에 공백이 있는 디렉터리로 보내시겠습니까?

소프트 링크를 생성하려는 파일 이름 목록이 있습니다(라고 부르세요 filenames.txt). 그것을 사용할 때

cat filenames.txt | awk 'system("ln -s "$0" ~/directory\ with\ spaces/subdirectory/")`

공백(파일 이름 또는 대상 디렉터리)이 있는 항목이 있으면 막히는 것 같습니다.

공백이 없는지 확인하기 위해 먼저 모든 이름을 바꾸지 않고 이러한 소프트 링크를 만들려면 어떻게 해야 합니까?

답변1

참고: 여기에 제공된 모든 예제는 파일 및 파일 이름 목록이 있는 디렉터리에서 실행됩니다. 예를 들어 /mnt/Hard\ Drive/some\ files/폴더 에 있는 경우 filenames.txt해당 폴더에도 저장되어 있는지 확인하세요. 셸에서 명령을 실행할 때 cd /mnt/Hard\ Drive/some\ files/예제를 먼저 실행하세요.

AWK

AWK의 시스템 호출은 공백이 포함된 항목에 사용할 수 있지만 조금 더 복잡합니다. 원본 코드에 구문 오류가 있어 cat쓸모가 없습니다. 이 사실에 관계없이 올바른 접근 방식은 먼저 명령을 sprintf()변수에 작성한 다음 해당 변수를 에 전달하는 것 입니다 system().

$ ls dir\ with\ spaces/                                                                                                  
$ awk '{cmd=sprintf("fpath=$(realpath -e \"%s\" );%s %s \"$fpath\" \"%s\"",$0,"ln","-s","dir with spaces/");system(cmd) }' filenames.txt                   
$ ls dir\ with\ spaces/                                                                                                  
ascii.txt@  disksinfo.txt@  input.txt@  process info.txt@

BASH(또는 Bourne과 유사한 쉘)

저항이 덜한 경로는 아래에 제공된 작은 쉘 스크립트를 사용하는 것입니다.

#!/bin/bash
# uncomment set -x for debugging
#set -x 

# Preferably, the directory should be full path
dir="dir with spaces/"

while IFS= read -r line
do
    if [ "x$line" != "x"  ] && [ -e "$line" ];
    then
        fpath=$( realpath -e "$line" )
        ln -s "$fpath" "$dir"
    fi
done < filenames.txt

실행 중인 스크립트:

$ ls -l dir\ with\ spaces/                                                                                        
total 0
$ cat filenames.txt                                                                                               
input.txt
ascii.txt
disksinfo.txt
process info.txt
$ ./makelinks.sh                                                                                                  
$ ls -l dir\ with\ spaces/                                                                                        
total 0
lrwxrwxrwx 1 xieerqi xieerqi  9 1月  15 00:47 ascii.txt -> ascii.txt
lrwxrwxrwx 1 xieerqi xieerqi 13 1月  15 00:47 disksinfo.txt -> disksinfo.txt
lrwxrwxrwx 1 xieerqi xieerqi  9 1月  15 00:47 input.txt -> input.txt
lrwxrwxrwx 1 xieerqi xieerqi 16 1月  15 00:47 process info.txt -> process info.txt

작동 방식은 간단합니다. while IFS= read -r line; do . . . done < filenames.txtfilenames.txt를 한 줄씩 읽고 각 줄은 line변수로 들어갑니다. 해당 줄이 비어 있지 않은지, 파일이 존재하는지 확인합니다(스크립트는 원본 파일과 파일 목록이 있는 동일한 디렉터리에서 실행됩니다). 두 조건이 모두 참이면 링크를 설정합니다.

파일이 개행 문자로 끝나는지 확인해야 합니다. 마지막 행이 개행 문자로 끝나지 않으면(실제로 발생함) 마지막 행을 건너뛰므로 파일에 대한 링크가 생성되지 않습니다.

이상적이지도 않고 단점도 없지는 않지만, system()이는 awk를 사용할 수 없는 경우(요즘에는 거의 드물지만)에 실행 가능한 솔루션입니다.

매개변수

약간 더 간단한 쉘 접근 방식은 via xargsbash -c '' sh다시 플래그를 사용하여 filenames.txt를 한 줄씩 읽고 -L1각 줄을 bash에 대한 인수로 사용하는 것입니다. 를 사용하여 $@전체 명령줄 인수 배열($1, $2, $3...등)을 문자열 변수로 변환한 다음 에 전달합니다 ln. 마지막으로 변수가 sh설정됩니다 $0. 이것이 필요한지 여부는 논쟁의 여지가 있지만 이 질문의 주제는 아니므로 건너뛰겠습니다 :)

$ xargs -I {} -L1 bash -c 'file="$@";fpath=$(realpath -e "$file"); ln -s "$fpath"  ./dir\ with\ spaces/"$file" ' sh < filenames.txt                      
$ ls dir\ with\ spaces/                                                                                                  
ascii.txt@  disksinfo.txt@  input.txt@  process info.txt@

파이썬

$ ls dir\ with\ spaces/ 
$ python -c "import os,sys;fl=[f.strip() for f in sys.stdin];map(lambda x: os.symlink(os.path.realpath(x),'./dir with spaces/' + x),fl)" < filenames.txt
$ ls dir\ with\ spaces/                                                                                                  
ascii.txt@  disksinfo.txt@  input.txt@  process info.txt@

작동 방식은 간단합니다. filenames.txtpython으로 리디렉션 stdin하고 각 줄을 목록으로 읽은 fl다음 os.symlink()목록의 각 항목을 실행합니다. 줄이 길지만 효과적입니다.

진주

Perl에서는 더 짧은 버전이 구현됩니다.

$ perl -lane 'use Cwd "realpath";symlink(realpath($_),"./dir with spaces/" . $_)' < filenames.txt                                                     
$ ls dir\ with\ spaces/                                                                                                  
ascii.txt@  disksinfo.txt@  input.txt@  process info.txt@

답변2

system()이러한 작업에는 외부 명령이나 유사한 기능을 호출하는 스크립트 언어를 사용하지 마십시오 . 그것은 같은 것을 제시한다위험과 함정Bourne Shell과 같은 eval.

xargs안전하고 효율적으로 이 작업을 수행할 수 있습니다.

xargs -d '\n' ln -s -t ~/directory\ with\ spaces/subdirectory/ < filenames.txt

filenames.txt이것이 하는 일은 인수로 제공하는 명령의 끝에 무언가를 추가하여 명령을 작성하는 것입니다. 그런 다음 실행합니다. 위의 예에서 빌드할 명령은 다음과 같습니다.

ln -s -t directory file1 file2 ... fileN

흥미롭게도 filenames.txt에서 아무 것도 참조할 필요가 없으며 디렉터리에 대한 중첩된 참조를 사용할 필요도 없습니다.

답변3

문자열 리터럴에서 이중 백슬래시를 사용하여 셸에 전달된 문자열에 단일 백슬래시를 가져오면 셸이 다음 문자를 문자 그대로 해석하게 됩니다.

awk 'system("… ~/directory\\ with\\ spaces/subdirectory/")'

셸 조각에 사용된 입력을 인용하는 한 가지 방법은 각 문자 앞에 백슬래시를 추가하는 것입니다. Portable awk로는 이것을 쉽게 할 수 없습니다(GNU awk에는 이 작업을 수행하는 확장 기능이 있습니다). 또는 각 요소 주위에 작은따옴표를 배치하고 요소의 모든 작은따옴표를 로 바꿉니다 '\''. Awk를 사용하면 이 솔루션이 쉬워집니다 gsub. awk 코드 조각이 쉘 스크립트에 있는 경우 백슬래시 8진수 이스케이프를 사용하여 awk 문자열 리터럴에 작은따옴표를 넣을 수 있습니다.

<filenames.txt awk '{
    gsub(/\047/, /\047\\\047\047/, $0);
    system("ln -s \047" $0 "\047 ~/directory\\ with\\ spaces/subdirectory/");
}'

관련 정보