rm $ (ls)를 사용하여 파일을 삭제하면 단점이 있습니까?


13

내가 사용하는 경우 궁금 해서요 rm $(ls)(또는 파일 삭제 rm -r $(ls)안전하다고 아니라 삭제 디렉토리에를)? 모든 웹 사이트에서 사람들은이 명령이 다른 명령보다 훨씬 쉬운 것처럼 보이지만 다른 방법을 제공하기 때문에.


3
기본 답변입니다. ls는 특수 문자를 처리 할 수 ​​없습니다. 좀 더 자세히 설명하기 위해 답을 쓸 수 있습니다.
Sergiy Kolodyazhnyy

5
touch 'foo -r .. bar'; rm $(ls); 부모 디렉토리는 어디로 갔습니까? 또한 이보다 더 복잡한 대안은 무엇입니까? rm *타이핑하고 생각하기가 훨씬 쉽고 안전합니다 (그러나 완벽하게 안전하지는 않습니다. Dennis의 답변 참조).
Peter Cordes

1
탁월한 답변 외에도 ls구현마다 다를 수 있으므로 표준이 아닙니다. 당신이 필요에 따라 다음과 같은 대안을 고려 find하고 stat. ls사람이 소비 할 때만 사용해야하며 다른 명령이나 스크립트에서 사용 해서는 안됩니다 .
Paddy Landau

답변:


7

이것이 무엇을 의도합니까?

  • ls 현재 디렉토리에 파일을 나열합니다
  • $(ls)ls대한 인수 로 장소의 출력을 대체rm
  • 기본적 rm $(ls)으로 현재 디렉토리의 모든 파일을 삭제하도록 설계되었습니다

이 사진에 무슨 문제가 있습니까?

ls파일 이름에서 특수 문자를 올바르게 처리 할 수 ​​없습니다. 유닉스 사용자는 일반적으로 다른 접근 방식을 사용하는 것이 좋습니다 . 또한 filenames 계산 에 대한 관련 질문 에서이를 보여주었습니다 . 예를 들어 :

$ touch file$'\n'name                                                                                                    
$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ 

또한 Denis의 답변에서 올바르게 언급 된 것처럼 대시가있는 파일 이름은 rm대체 후의 인수로 해석 될 수 있으므로 파일 이름 제거의 목적을 상실합니다.

작동하는 것

현재 디렉토리에서 파일을 삭제하려고합니다. 따라서 glob를 사용하십시오 rm *:

$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ rm *
$ ls
$ 

find명령 을 사용할 수 있습니다 . 이 도구는 현재 디렉토리뿐만 아니라 전체 디렉토리 트리를 재귀 적으로 탐색하고 다음을 통해 파일에서 작업 할 수 있습니다.-exec . . .{} \;

$ touch "file name"                                
$ find . -maxdepth 1 -mindepth 1                                                                                         
./file name
$ find . -maxdepth 1 -mindepth 1 -exec rm {} \;                                                                          
$ ls
$ 

(이 단지 파일이라고 메모를 사용해야합니다, 파이썬은 우리가뿐만 아니라 그것을 사용 할 수 있도록 파일 이름에 특수 문자와 함께 문제가되지 않습니다 os.rmdir()그리고 os.path.isdir()당신이 디렉토리에서 작동 할 경우)

python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

실제로, 위의 명령 ~/.bashrc은 간결성 을 위해 기능 또는 별명으로 전환 될 수 있습니다 . 예를 들어

rm_stuff()
{
    # Clears all files in the current working directory
    python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

}

펄 버전은

perl -e 'use Cwd;my $d=cwd();opendir(DIR,$d); while ( my $f = readdir(DIR)){ unlink $f;}; closedir(DIR)'

1
귀하의 "$(ls)"디렉토리에 하나의 파일 만있을 경우에만 작동합니다. 파일 완성이 유일한 완료이기 때문에 탭 완성을 사용하여 파일 이름을 확장 할 수도 있습니다.
Peter Cordes

@PeterCordes는 실제로 이상한 이유로 하나의 파일에서만 작동합니다. 하나 더 인수가 사용에있어 그 ls후 : 나는 그것을 편집 할 수 있습니다
세르지 Kolodyazhnyy

나는 그것을 이상한 이유라고 부르지 않을 것입니다 : 당신은 $(ls)단어 분리를 비활성화하기 위해 인용 하거나 단어 분리가 일어날 수있게합니다 (재앙 결과). 데이터를 코드로 취급하지 않고 여러 문자열 목록을 전달하는 유일한 방법은 배열 변수 또는 \0구분 기호를 사용하는 것이지만 셸 자체는 그렇게 할 수 없습니다. 여전히 IFS=$'\n'가장 위험하지는 않지만 일치하지 않습니다 find -print0 | xargs -0. 또는 grep -l --null. 또는와 같은 문제로 전체 문제를 피하십시오 find -exec rm {} +. ( +rm의 각 호출에 여러 인수를 전달하는 방법에 유의하십시오 . WAY가 더 효율적입니다).
Peter Cordes

@PeterCordes Yup은 완전히 동의했습니다. 그러나 IFS=$'\n'파일 이름에 줄 바꿈이 있기 때문에이 경우에도 실패하므로 단어를 쪼개면 파일 이름이 아닌 두 개의 파일 이름으로 처리됩니다. 그러나 이상한 이유 IFS는 space, tab, newline, default 인 기본값으로 rm "$(ls)"도 실패해야한다는 것입니다. 파일 이름을 두 개의 별도 파일로 말한 것처럼 처리해야하지만 그렇지 않습니다. 일반적으로 내가 사용 find과 함께 -exec, 또는 find . . .-print0 | while IFS= read -d'' FILENAME ; do . . . done파일 이름을 처리하는 구조. 또는 하나를 사용할 수 있습니다 python. 예를 추가했습니다.
Sergiy Kolodyazhnyy

"$(ls)"따옴표 $(ls)는 단어 분리로부터 의 확장을 보호하기 때문에 항상 하나의 인수로 확장됩니다 . 그들이 보호하는 것처럼 "$foo".
Peter Cordes

26

아니요, 안전하지 않으며 일반적으로 사용되는 대안 rm *이 훨씬 안전하지 않습니다.

많은 문제가 rm $(ls)있습니다. 다른 사람들이 이미 답변을 다루었으므로 출력은 내부 필드 구분 기호ls 에있는 문자로 나뉩니다 .

가장 좋은 시나리오는 작동하지 않습니다. 최악의 시나리오에서는 파일 만 제거하고 (디렉토리는 제외) 일부 파일은 선택적으로 제거 -i하려고하지만 c -rf현재 디렉토리에 이름이있는 파일이 있습니다. 어떻게되는지 보자.

$ mkdir a
$ touch b
$ touch 'c -rf'
$ rm -i $(ls)
$ ls
c -rf

이 명령 rm -i $(ls)은 파일 만 제거하고 각 파일을 제거하기 전에 요청했지만 결국 실행 된 명령은 읽습니다.

rm -i a b c -rf

그래서 그것은 완전히 다른 일을했습니다.

참고 rm *변두리에 더 나은 아니라. 이전과 같이 디렉토리 구조를 사용하면 여기에서 의도 한대로 작동하지만라는 파일이 있으면 -rf여전히 운이 좋지 않습니다.

$ mkdir a
$ touch b
$ touch ./-rf
$ rm -i *
$ ls
-rf

더 나은 대안이 몇 가지 있습니다. 가장 쉬운 방법은 rm 과 globbing입니다.

  • 명령

    rm -- *
    

    의도 한대로 정확하게 작동하며, --그 이후의 모든 것이 옵션으로 해석되어서는 안된다는 신호를 보냅니다.

    이것은 현재 20여 년 동안 POSIX 유틸리티 구문 지침의 일부였습니다. 널리 퍼져 있지만 어디에나 존재할 것으로 기 대해서는 안됩니다.

  • 명령

    rm ./*
    

    glob가 다르게 확장되므로 호출 된 유틸리티의 지원이 필요하지 않습니다.

    위의 예제에서 echo 앞에 추가하여 실행되는 명령을 볼 수 있습니다 .

    $ echo rm ./*
    rm ./a ./b ./-rf
    

    선행은 rm 이 실수로 옵션과 같은 파일 이름을 처리하지 ./못하게 합니다.


1
아주 좋은 점은 확장 후의 파일 이름이 플래그가된다는 것 rm입니다. +1
Sergiy Kolodyazhnyy

1
심지어 나 스티어 : touch 'foo -rf .. bar. ls의 출력 에서 경로 구분 기호를 만들 수 없다면 공격자는 상위 디렉토리보다 더 높은 값을 얻을 수 있다고 생각하지 않습니다 .
Peter Cordes

@Peter 공격자는 특허 디렉토리를 제거하려면 쓰기 권한이 있어야합니다.
Sergiy Kolodyazhnyy

1
@PeterCordes rm의 모든 버전 에이 안전 장치가 있는지 확실하지 않지만 Ubuntu, openSUSE 및 Fedora rm: refusing to remove '.' or '..' directory: skipping '..'에서 부모 디렉토리를 제거하려고 할 때 이와 유사한 메시지가 표시됩니다.
Dennis

@Serg : 공격자는 파일 이름이 포함 된 .zip 파일을 전송 한 후 압축을 풀고 내용을 삭제하려고 시도합니다. 또는 / var / tmp 또는 다른 이름으로 해당 파일 이름을 만듭니다.
Peter Cordes
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.