"mv"가 자동으로 실패하게하는 방법이 있습니까?


61

mv foo* ~/bar/파일이 일치하지 않으면 같은 명령 이 stderr에이 메시지를 생성합니다 foo*.

mv: cannot stat `foo*': No such file or directory

그러나 스크립트 에서이 작업을 수행하는 것이 좋습니다. 로그에서 해당 메시지를 생략하고 싶습니다.

mv아무것도 움직이지 않아도 조용히 말할 수 있는 좋은 방법이 있습니까?


13
mv foo* ~/bar/ 2> /dev/null?
Thomas Nyman

1
그래 에 대한 일부 스위치와 같이 마음에 "가장 똑똑한"것이 있다고 생각합니다 mv. :) 물론 그럴 것입니다.
Jonik

3
일부 mv구현에서이를 비활성화하는 -q옵션을 지원하는 것을 보았지만 에 대한 POSIX 사양의 일부는 아닙니다 mv. 는 mvGNU의로 coreutils에, 예를 들어, 않습니다 하지 같은 옵션이 있습니다.
Thomas Nyman

글쎄, 나는 문제가 "mv"를 조용하게 유지하는 방법이라고 생각하지 않지만, 더 올바르게하는 방법입니다. 파일 / 디렉토리 foo *가 있는지, 사용자에게 쓰기 가능한 권한이 있는지 확인한 후 "mv"를 실행 하시겠습니까?
Shâu Shắc

답변:


46

이것을 찾고 있습니까?

$ mv  file dir/
mv: cannot stat file’: No such file or directory
$ mv  file dir/ 2>/dev/null
# <---- Silent ----->

20
참고로 반환 값은 여전히! = 0이므로 모든 쉘 스크립트 set -e에서 사용해야하는 것은 실패합니다. 단일 명령에 대한 검사를 비활성화하기 위해 a 를 추가 할 수 있습니다 . || true
reto

10
set -e모든 쉘 스크립트에서 @reto를 사용 해서는 안되며, 대부분의 경우 오류 관리 기능이없는 것보다 훨씬 복잡합니다.
Chris Down

1
@ChrisDown 당신의 요점을 참조하십시오. 일반적으로 두 가지 악 중에서 선택합니다. 그러나 의심스러운 경우 성공적인 실패보다 성공적인 실행 실패를 선호합니다. (고객 / 사용자가 볼 수있을 때까지는 알 수 없습니다). 쉘 스크립트는 초보자에게 큰 광산 분야이므로 오류를 놓치기가 쉽고 스크립트가 무너져갑니다!
reto

14

실제로, 나는 뮤팅 mv이 좋은 접근 방법 이라고 생각하지 않습니다 (관심있는 다른 것들에 대해서도보고 할 수 있음을 기억하십시오 ... 누락 ~/bar). glob 표현식이 결과를 반환하지 않는 경우에만 음소거하고 싶습니다. 실제로 오히려 전혀 실행하지 마십시오.

[ -n "$(shopt -s nullglob; echo foo*)" ] && mv foo* ~/bar/

매우 매력적으로 보이지 않으며에서 작동합니다 bash.

또는

[ 'foo*' = "$(echo foo*)" ] || mv foo* ~/bar/

당신이 세트 에 bash있는 것을 제외하고 만 nullglob. 그래 브 패턴의 3 배 반복 가격을 지불합니다.


두 번째 형태의 사소한 문제 : foo*현재 디렉토리에서 glob와 일치하는 유일한 파일 일 때 이름이 지정된 파일을 이동하지 못합니다 foo*. 이것은 말 그대로, 터프한 글로 표현되지 않는 glob 표현으로 해결할 수 있습니다. 예 [ 'fo[o]*' = "$(echo fo[o]*)" ] || mv fo[o]* ~/bar/.
fra-san

13

find . -maxdepth 1 -name 'foo*' -type f -print0 | xargs -0r mv -t ~/bar/

— GNU mv에는 "대상 우선"옵션 ( -t)이 있으며 xargs입력이 없으면 명령 실행을 건너 뛸 수 있습니다 ( -r). 의 사용 -print0-0상응하여 파일 이름에 공백이 다른 "재미"물건을 포함 할 때 엉망이되지 않을 것 확인합니다.


2
그 주 -maxdepth, -print0, -0-r(그들 중 일부는 현재 다른 구현에서 발견된다하더라도) 또한 GNU 확장입니다.
Stéphane Chazelas

1
Poige, 많은 사용자가 Linux를 사용하지 않습니다. 대답의 어떤 부분이 이식 가능하지 않은지를 지적하는 것이 여기에 표준 관행이며 매우 도움이됩니다. 이 사이트를 " Unix & Linux"라고하며 많은 사람들이 특정 형태의 BSD 또는 Unix, AIX 또는 macOS 또는 임베디드 시스템을 사용합니다. 개인적으로 가져 가지 마십시오.
terdon

나는 개인적으로 아무것도 복용하지 않습니다. 나는 당신이 지금 본 것처럼 제거했다는 의견을 가지고 있습니다. 나는 반복한다. 나는 그 의견이 "GNU라는 점에 유의한다"는 것이 무의미하다고 생각한다. 내 대답에 GNU 버전을 기반으로한다는 명확한 표시가 있기 때문에 그들은 전혀 추가 할 가치가 없습니다 mv.
poige

5

실제로는 foo*해당 파일 이름을 일치하는 파일 이름 목록으로 확장하는 쉘 이므로, mv자체적으로 수행 할 수있는 작업은 거의 없습니다.

여기서 문제는 glob가 일치하지 않을 때 bash(대부분의 다른 Bourne과 같은 쉘, 버그가있는 동작이 실제로 70 년대 후반 Bourne 쉘에 의해 도입 됨) 일부 패턴을 명령에 전달한다는 것입니다.

따라서 여기에서 foo*어떤 파일과도 일치하지 않으면 (Bourne 이전 쉘 및 여러 현대 쉘과 같이) 명령을 중단하는 대신 쉘은 그대로의 foo*파일을로 전달하여 mv기본적으로 mv라는 파일을 이동하도록 요청 합니다 foo*.

해당 파일이 없습니다. 일치하면 실제로 패턴과 일치했을 것이므로 mv오류를보고합니다. 패턴이 foo[xy]대신 mv발생한 foo[xy]경우 fooxfooy파일 대신 실수로 파일을 이동했을 수 있습니다 .

이제 문제가없는 쉘 (pre-Bourne, csh, tcsh, fish, zsh, bash -O failglob)에서도 여전히 오류가 발생 mv foo* ~/bar하지만 이번에는 쉘 에서 오류가 발생합니다 .

일치하는 파일이없는 경우 오류가 아닌 것으로 간주 하고이 경우 foo*아무것도 움직이지 않으려면 먼저 파일 목록을 작성하십시오 ( nullglob옵션 옵션을 사용하여 오류가 발생하지 않는 방식 으로) 일부 쉘), 그리고 호출 만 mv목록이 비어 있지 않습니다.

즉, 숨어보다 더 나은 것 모두 의 오류를 mv(추가하는 2> /dev/null것처럼 것) mv다른 이유로 실패, 당신은 아마 아직도 이유를 알고 싶어.

zsh에서

files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/

또는 임시 변수를 사용하지 않으려면 익명 함수를 사용하십시오.

() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)

zshBourne 버그가 없으며 glob가 일치하지 않고 nullglob옵션이 활성화 되지 않은 경우 명령을 실행하지 않고 오류를보고하는 쉘 중 하나 이므로 zsh오류를 숨기고 복원 할 수 있습니다 stderr mv때문에 mv일치하지 않는 globs에 대한 오류는 없지만 여전히 오류가 표시됩니다.

(mv 2>&3 foo* ~/bar/) 3>&2 2>&-

또는 glob가 너무 많은 파일로 확장 zargs되면 문제를 피할 수 foo*있습니다.

autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option

ksh93에서 :

files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/

bash에서 :

bashnullglob하나의 glob에만 사용할 수있는 구문이 없으며 failglob옵션이 취소 nullglob되므로 다음과 같은 것이 필요합니다.

saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"

또는 서브 쉘에서 옵션을 설정하여 저장하기 전에 저장 한 후 나중에 복원해야합니다.

(
  shopt -s nullglob
  shopt -u failglob
  files=(foo*)
  ((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)

에서 yash

(
  set -o nullglob
  files=(foo*)
  [ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)

에서 fish

피쉬 쉘에서 nullglob 동작은이 set명령 의 기본값 이므로 다음과 같습니다.

set files foo*
count $files > /dev/null; and mv -- $files ~/bar/

POSIXly

nullglobPOSIX 에는 옵션 이 없으며 sh위치 매개 변수 이외의 배열은 없습니다. 그래 브가 일치하는지 여부를 감지하는 데 사용할 수있는 트릭이 있습니다.

set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
  shift
  mv -- "$@" ~/bar/
fi

a foo[*]foo*glob 을 모두 사용하여 일치하는 파일이없는 경우와 호출 할 수있는 하나의 파일이있는 경우 foo*(할 set -- foo*수없는)를 구별 할 수 있습니다 .

더 많은 독서 :


3

아마도 가장 좋지는 않지만 find명령을 사용 하여 폴더가 비어 있는지 여부를 확인할 수 있습니다 .

find "foo*" -type f -exec mv {} ~/bar/ \;

1
이렇게하면 리터럴 foo*검색 경로 가 제공 됩니다 find.
Kusalananda

3

이 오류는 bash의 동작에 따라 일치하지 않는 globs를 확장하기 때문에 bash를 사용한다고 가정합니다. (비교하여, zsh는 일치하지 않는 glob를 확장하려고 할 때 오류를 발생시킵니다.)

그렇다면 다음 해결 방법은 어떻습니까?

ls -d foo* >/dev/null 2>&1 && mv foo* ~/bar/

이것은 자동으로 무시 mv하는 경우 ls -d foo*경우 여전히 오류를 기록하면서 실패 ls foo*성공하지만이 mv실패합니다. ( 권한 부족, FS 관련 문제 등) 존재하지 않는 ls foo*다른 이유로 실패 foo*할 수 있으므로이 솔루션에서는 이러한 조건을 자동으로 무시합니다.


나는 주로 bash에 관심이 있습니다 (예 bash. 질문에 태그를 지정했습니다 ). 존재하지 않는 mv다른 이유로 실패 할 수 있는 사실을 고려하여 +1 foo*
Jonik

1
(실제로 나는 그것이 장황하기 때문에 이것을 사용하지 않을 것이고, ls명령 의 요점은 적어도 독자없이, 적어도 의견이 없으면 명확하지 않을 것입니다.)
Jonik

ls -d foo*ln -s /nowhere foobar(적어도 일부 ls구현에서는) 후와 같은 다른 이유로 인해 0이 아닌 종료 상태로 반환 될 수 있습니다.
Stéphane Chazelas

3

예를 들어 할 수 있습니다

mv 1>/dev/null 2>&1 foo* ~/bar/ 또는 mv foo* ~/bar/ 1&>2

자세한 내용은 다음을 참조하십시오 : http://mywiki.wooledge.org/BashFAQ/055


mv foo* ~/bar/ 1&>2명령을 침묵시키지 않고 stdout에 있었던 것을 stderr에도 보냅니다.
Chris Down

당신이 (나 같은) 창에서와 Mingw를 사용하는 경우 및 교체 /dev/null와 함께NUL
리안 샌더슨

이 명령은 어떻게 작동합니까? 앰퍼샌드는 무엇을합니까? 무엇 12의미합니까?
Aaron Franke

@AaronFranke 1은 Linux 프로세스가 작성 될 때 기본적으로 stdout이고 2는 stderr 파이프입니다. Linux 명령의 파이프에 대해 자세히 알아보십시오. 여기서 시작할 수 있습니다 -digitalocean.com/community/tutorials/…
Mithun B

2

당신은 (이동식으로) 속임수를 쓸 수 있습니다 perl:

perl -e 'system "mv foo* ~/bar/" if glob "foo*"'

downvoting 전에 의견을 주시기 바랍니다.
Joseph R.

2

Perl을 사용하려면 다음과 같이 진행하십시오.

#!/usr/bin/perl
use strict;
use warnings;
use File::Copy;

my $target = "$ENV{HOME}/bar/";

foreach my $file (<foo*>) {
    move $file, $target  or warn "Error moving $file to $target: $!\n";
}

또는 단일 라이너로 :

perl -MFile::Copy -E 'move $_, "$ENV{HOME}/bar/" or warn "$_: $!\n" for <foo*>'

move명령에 대한 자세한 내용은 File :: Copy 설명서를 참조하십시오 .


-1

대신에

mv foo* ~/bar/

넌 할 수있어

cp foo* ~/bar/
rm foo* 

간단하고 읽기 쉬운 :)


6
복사 한 다음 제거하는 것은 단순한 이동보다 시간이 오래 걸립니다. 파일이 사용 가능한 디스크 공간보다 큰 경우에도 작동하지 않습니다.
Anthon

11
OP는 조용한 솔루션을 원합니다. 존재하지 않으면 침묵 하지도 cp않고 rm침묵 하지도 foo*않습니다.
ron rothman

-1
mv foo* ~/bar/ 2>/dev/null

위의 명령이 성공했는지 여부는 이전 명령의 종료 상태로 찾을 수 있습니다

command: echo $?

echo $의 출력이라면? 0 이외의 값은 출력이 0이면 명령이 성공하지 못한 경우 명령이 실패 함을 의미합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.