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

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


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.