shellcheck는 basename을 사용하지 말 것을 권고합니다 : 왜?


25

shellcheck을 시도하고 있습니다 .

나는 그런 것을 가지고

basename "${OPENSSL}" 

그리고 나는 다음과 같은 제안을 받는다

Use parameter expansion instead, such as ${var##*/}.

실용적인 관점에서 나는 아무런 차이가 없다

$ export OPENSSL=/opt/local/bin/openssl
$ basename ${OPENSSL}
openssl
$ echo ${OPENSSL##*/}
openssl

이후 basename에 POSIX 사양 , 난 안가 가장 좋은 방법해야 이유를 않습니다. 힌트가 있습니까?


8
필요하지 않을 때 새로운 프로세스를 분기합니다.
— jordanm

@jordanm fair 충분히 ... 나는 효율성에 대해 생각하지 않았습니다.
— Matteo

3
@jordanm 반면에 그것은 bash 이외의 쉘에서 작동합니다
— Matteo

1
@JosephR. 그것이 내가 생각한 것이지만 작동하지 않는다는 것을 알았습니다 csh. cshPOSIX가 아닌 것 같습니다 .
— terdon

3
@terdon-csh는 POSIX와 매우 거리가 멀다.
— jordanm

답변:


25

효율성에 관한 것이 아니라 정확성에 관한 것입니다. basename줄 바꿈을 사용하여 인쇄 할 파일 이름을 구분합니다. 일반적인 경우 하나의 파일 이름 만 전달하면 출력에 후행 줄 바꿈이 추가됩니다. 파일 이름에는 개행 문자가 포함될 수 있으므로 이러한 파일 이름을 올바르게 처리하기가 어렵습니다.

사람들이 일반적으로 다음 basename과 같이 사용한다는 사실로 인해 더 복잡합니다 "$(basename "$file")". 모든 후행 줄 바꿈을에서 $(command)제거 하기 때문에 상황이 더욱 어려워 집니다 . 줄 바꿈으로 끝나는 경우를 고려하십시오 . 그런 다음 줄 바꿈을 추가하지만 두 줄 바꿈을 모두 제거 하면 파일 이름이 잘못됩니다.command$filebasename"$(basename "$file")"

또 다른 문제는 basename경우이다 $file로 시작할 -(마이너스 일명 대시)가 옵션으로 해석됩니다. 이것은 쉽게 고칠 수 있습니다.$(basename -- "$file")

강력한 사용법 basename은 다음과 같습니다.

# A file with three trailing newlines.
file=$'/tmp/evil\n\n\n'

# Add an 'x' so we can tell where $file's newlines end and basename's begin.
file_x="$(basename -- "$file"; printf x)"

# Strip off two trailing characters: the 'x' added by us and the newline added by basename. 
base="${file_x%??}"

대안은을 사용 ${file##*/}하는 것인데, 더 쉽지만 자체 버그가 있습니다. 특히 $fileis /또는 인 경우에는 잘못되었습니다 foo/.


2
아주 좋은 점수, +1 OP가 내 대신 이것을 받아 들여서 다행입니다.
— terdon

2
대위법 : 어떤 경우 $file이다 foo/? 만약 그렇다면 /?
— Gilles 'SO- 악마 중지

2
@Gilles : 네 말이 맞아. basename결국 해키가 접근하는 것이 더 좋다고 생각하기 시작 했습니다. 내가 생각해 낼 수있는 가장 좋은 대안은 ${${${file}%%/#}##*/}and입니다 [[ $file =~ "([^/]*)/*$" ]] && printf "%s" $match[1]. 둘 다 zsh 관련이며 /올바르게 처리되지 않습니다 .
— Matt

@terdon 당신이 개인적으로 그것을하지 않았다 :-) 감사합니다. 줄 바꿈이있는 파일은 일반적이지 않지만 Matt은 요점을 가지고 있습니다. 물론 변수 대체를 사용하는 것이 더 효율적입니다.
— Matteo

16

의 관련 라인 shellcheck의 소스 코드 입니다 :

checkNeedlessCommands (T_SimpleCommand id _ (w:_)) | w `isCommand` "dirname" =
    style id "Use parameter expansion instead, such as ${var%/*}."
checkNeedlessCommands (T_SimpleCommand id _ (w:_)) | w `isCommand` "basename" =
    style id "Use parameter expansion instead, such as ${var##*/}."
checkNeedlessCommands _ = return ()

명시 적으로 설명되어 있지는 않지만 함수 이름 ( checkNeedlessCommands)을 기반으로하는 @jordanm이 옳은 것처럼 보이고 새로운 프로세스를 포기하지 않는 것이 좋습니다.


7
소스가 당신과 함께 있기를 바랍니다 :)
— Joseph R.

3

dirname, basename, readlink등 - 보안이 중요한 될 때 이식성 문제를 만들 수 있습니다 (감사 @Marco이가 수정) (경로의 보안을 필요로). Fedora Linux와 같은 많은 시스템은 시스템을 배치하고 /bin다른 시스템 (예 : Mac OSX)은 시스템을 배치합니다 /usr/bin. 그런 다음 Windows에는 Bash가 있습니다 (예 : cygwin, msys 등). 가능하면 항상 순수한 배쉬를 유지하는 것이 좋습니다. (@Marco 의견 당)

BTW, shellcheck에 대한 포인터 덕분에 이전에는 보지 못했습니다.


1
1)“보안”은 무엇을 의미합니까? 정교하게 할 수 있습니까? 2) OP는 전혀 언급하지 않았다 dirname. 3) 기본 핵심 유틸리티는 저장되어있는 PATH에 있어야합니다. basenamePATH에없는 시스템 은 아직 보지 못했습니다 . 4) bash를 사용할 수 있다고 가정하면 이식성 문제입니다. 이식성이 필요한 경우 항상 bash에서 벗어나 POSIX 쉘을 사용하는 것이 좋습니다.
— Marco

@Marco이 문제를 지적 해 주셔서 감사합니다. 예, 유틸리티가 경로에 있는지 확인하십시오. 그러나 Bash 스크립트에 추가 보안을 제공하려는 경우 절대 경로를 제공하는 것이 좋습니다. 따라서 '/ bin / basename'을 호출하면 RedHat 시스템에서 작동하지만 Mac에서는 오류가 발생합니다. 이것은 600 페이지 중 약 1/4 이이 주제에 관한 Bash Cookbook 에서 더 잘 설명됩니다 . 우리는 무료 소스 인 secure-lib 에서 보안 문제를 해결하기 위해 대략적인 노력을 기울 였습니다.
— AsymLabs

@Marco Point 4는 유효한 주석이지만 질문 (OP)은 sh / bash 스크립트를 모두 검사하도록 설계된 shellcheck로 시작하고 작성됩니다 . 따라서 기본적으로 Posix가 아니라고 가정해야합니다.
— AsymLabs

경로 환경 변수의 @Marco Security는 쉽게 손상 될 수 있습니다. 예를 들어, 몇 년 전에 로컬에 설치된 Ruby RVM이 기본적으로 시스템 경로 앞에 경로를 배치했다는 사실에 놀랐습니다.
— AsymLabs

OP는 구체적으로 POSIX를 언급하고 질문에는 태그가 posix있고 태그는 없습니다 bash. OP에 bash 솔루션이 필요하다는 표시를 찾지 못했습니다. "순수한 배쉬를 유지하는 것이 항상 낫다"라는 말은 잘못되었습니다. 죄송합니다.
— Marco
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.