"ls"에서 "mv"로 파이프를 만듭니다.

"ls"에서 "mv"로 파이프를 만듭니다.

명령줄에서 작은 유틸리티를 만들려고 합니다. 현재 작업 디렉터리에 대해 최근에 추가된 파일을 찾아 mv인수로 지정된 이름을 지정하고 싶습니다.

나는 다음을 통해 이것을 달성하려고 노력해 왔습니다. ls -tr | tail -n 1 | xargs -0 -J % mv % SQL_warning_2.png

...하지만 bash는 다음과 같이 보고합니다.

mv: rename Screenshot 2020-06-22 at 17.53.23.png
 to SQL_warning_2.png: No such file or directory

나는 이것이 xargs가 filename:의 공백을 별도의 인수로 전달한다는 것을 의미한다고 생각하지만, 내가 아는 한 이에 대한 플래그 Screenshot 2020-06-22 at 17.53.23.png는 없습니다 -print0.ls

내 가정 중 일부가 잘못된 경우 용서해 주십시오. 저는 xargs에 대해 매우 익숙하지 않습니다.

답변1

파일 이름에 개행 문자가 포함되어 있지 않다고 가정하는 것 같으므로(다음을 tail사용하여 하나를 선택합니다.)

ls -tr | tail -n 1 | xargs -I % mv -- % SQL_warning_2.png

이렇게 하면 유틸리티가 한 번 호출 -I %됩니다 .xargs각 라인%해당 줄의 텍스트를 읽고 바꿉니다. 대신 -J %공백으로 줄을 나누세요.

$ echo 1 2 3 | xargs -J {} printf '"%s"\n' {}
"1"
"2"
"3"
$ echo 1 2 3 | xargs -I {} printf '"%s"\n' {}
"1 2 3"

현재 디렉터리에서 가장 최근에 수정된 파일의 이름을 바꾸려는 것 같습니다. 셸을 사용하여 zsh다음을 수행할 수 있습니다 .

mv -- *(.om[1]) SQL_warning_2.png

mv가장 최근에 수정된 일반 파일 이름을 첫 번째 인수로 사용하여 호출됩니다 . om[1]mtime 타임스탬프(최신 항목부터 가장 오래된 항목까지)를 기준으로 이름 목록을 정렬하고 첫 번째 항목을 선택하는 glob 한정자 입니다 . 이전 점은 일반 파일만 선택합니다(디렉토리 등은 선택하지 않음).

에서 bash:

zsh -c 'mv -- *(.om[1]) SQL_warning_2.png'

답변2

나는 다음과 같이 할 것입니다 :

mv -- "$(ls -t|head -n1)" new_filename

(현재 디렉토리의 최신 파일 이름에 개행 문자가 포함되어 있지 않다고 가정합니다.)

답변3

예, 을 사용하려면 -0표준 xargs입력에 NUL로 구분된 목록이 필요합니다. 여기에서는 마지막 개행 다음에 최신 파일의 이름 부분을 입력하고 그 뒤에 개행을 입력합니다(개행은 파일 ls이름의 일부가 아닌 파일 이름으로 추가됩니다).

해당 입력에는 NUL이 없으므로 xargs전체 입력은 mv개행 문자를 포함하는 하나의 인수로 전달됩니다. 따라서 최신 파일 이름에 개행 문자가 하나만 포함되어 있고 파일 이름의 마지막 문자인 경우에만 작동합니다. .

여기서는 줄 바꿈으로 구분된 목록이 아닌 NUL로 구분된 목록을 출력하는지 확인해야 하지만 이를 수행하는 구현 ls은 없습니다 .ls

xargs또는 기본 입력 형식( 제외 -0) 으로 되돌려야 합니다 . 여기서 인수는 xargs공백(목록은 구현 및 가능한 로케일에 따라 다름) 또는 줄 바꿈으로 구분되고, 와 "..."'...'\이러한 문자와 서로를 이스케이프하는 데 사용됩니다. 동일한 쉘 연산자를 다른 방식으로 인용할 수 있습니다. 일부 xargs구현에서는 입력을 텍스트로 해석하려고 시도하지만 파일 이름에는 모든 바이트 값(NUL 및 제외)이 포함될 수 있으므로 /C 로케일에서도 이 작업을 수행해야 합니다 .

export LC_ALL=C
ls -td ./* | awk -v q="'" '
  {gsub(/"/, "\"\\\"\"")} # escape "
  NR == 1 {printf "\"%s", $0; next}
  /\// {exit} # a / means the start of the second file
  {printf "\"\\\n\"%s", $0} # escape newlines
  END {if (NR) print "\""}' |
  xargs -J % mv % newname

보시다시피, xargs안정적으로 사용하는 것은 엉덩이에 통증이 있습니다. 일부 xargs구현에서는 매개변수나 입력 라인의 크기에 대한 제한이 매우 낮습니다. 이것은 -J이식 불가능한 BSD 확장이라는 점에 유의하십시오 .

라인 기반 유틸리티를 사용하여 임의의 파일 이름을 처리하는 것도 ls매우 어렵습니다 .

zsh@Kusalananda가 제안한 것처럼 이를 사용하여 수정 시간별로 파일 목록을 정렬하는 것이 더 좋습니다 .

mv -- *(.om[1]) newname

에서는 bash다음 작업도 수행할 수 있습니다.

IFS= read -rd / newest < <(ls -td ./*) && newest=${newest%.}
newest=${newest%?} # strip newline
[ -n "$newest" ] && mv "$newest" newname

(ksh93 또는 zsh에서도 작동합니다). 이전 방법과 마찬가지로 목록의 두 번째 파일이 시작되는 줄을 결정하는 기능을 xargs사용합니다 ../*

관련 정보