Bash 섀도우 명령 - 명령과 동일한 이름을 가진 함수

Bash 섀도우 명령 - 명령과 동일한 이름을 가진 함수

.bashrc쓰기 불가능한 파일을 자동으로 sudos하는 기능이 있습니다 .

vim() {
    if [ -w "$1" ]; then
        \vim "$1"
    else
        sudo env HOME="$HOME" \vim -u ~/.vimrc "$1"
    fi
}

파일에 sudo가 필요한 경우 제대로 작동합니다. 그렇지 않은 경우 이 함수를 재귀적으로 호출하고 I CC까지 CPU를 100% 사용합니다.

이 답변에서여러 가지 옵션이 있으며 모두 시도해 보았습니다. 실제로 작동하는 것:

'vim' "$1" #fails
\vim "$1" #fails
command vim "$1" #Works!

다른 옵션이 예상대로 작동하지 않는 이유는 무엇입니까?

(중복인 건 알지만 현재 질문 제목으로는 SO/SE에서 답변을 찾기가 어렵기 때문에 나와 다른 사람들이 인터넷 검색을 통해 찾을 수 있는 제목으로 질문을 게시해야겠다고 생각했습니다.)

답변1

별칭 앞에 문자가 있으면 별칭이 실행되지 않습니다.

alias ls='ls -la'
ls foo.txt     #will run ls -la foo.txt
\ls foo.txt    #will run ls foo.txt
'ls' foo.txt   #will run ls foo.txt
'ls foo.txt'   #will run ls foo.txt

그러나 이것이 함수를 중지하는 것은 아니며, command동일한 이름의 함수를 생성하는 경우 기본 내장 함수를 참조해야 합니다.

ls () {
    echo "not an alias"
}
alias ls='echo "an alias"'

ls foo.txt          #will echo "an alias"
\ls foo.txt         #will echo "not an alias"
'ls' foo.txt        #will echo "not an alias"
command ls foo.txt  #will actually run `ls`

설명하다

문제는 처음 두 옵션이 별칭만 처리할 수 있다는 것입니다. 동일한 이름을 가진 함수나 명령이 있는지 감지하는 특수 리디렉션 연산자가 아닙니다. 별칭은 파이프나 명령의 첫 번째 부분인 경우에만 확장되므로 별칭 앞에 무언가를 넣는 것뿐입니다.

alias v='sudo vim'
v x.txt
#automatically expands "v" to "sudo vim" and then does the rest of the command
# v x.txt ==> sudo vim x.txt

Bash는 알고 있는 별칭 목록( 를 사용하여 얻을 수 있음)을 사용하여 alias명령의 첫 번째 단어를 확장 하려고 합니다. 별칭은 인수를 사용하지 않으며 첫 번째 단어(공백으로 구분)만 가능합니다.vim x.txt 에 익숙해sudo vimim x.txt올바르게 확장하려면 명령에서 위 별칭을 사용하여 확장하세요 .

그러나 작은따옴표 안의 내용은 확장되지 않습니다. 변수가 나타내는 내용 대신 echo '$USER'리터럴이 인쇄됩니다 . $USER또한 bash는 \x( x주로) 확장됩니다. 이는 별명을 이스케이프하기 위해 특별히 추가된 추가 메소드가 아니며 단지 bash 확장이 작성되는 방식의 일부일 뿐입니다. 따라서 이러한 방법을 사용하면 별칭이 명령을 숨기면서도 실제 명령에는 계속 액세스할 수 있습니다.

답변2

다른 옵션이 예상대로 작동하지 않는 이유는 무엇입니까?

명령줄이 실행되는 방법에 대한 세부 정보가 있기 때문입니다.

기본 개념은 명령줄의 첫 번째 단어가 쉘이 검색하여 실행하려고 하는 명령이라는 것입니다. 이 경우주문하다:

  1. 명령줄은 (대부분) 메타 문자로 구분됩니다.

    메타문자
    따옴표가 없을 때 단어를 구분하는 문자입니다. 다음 중 하나:
    | & ( ) < > 공백 탭 개행

  2. 첫 번째라면인용되지 않음word는 별칭 정의로 대체되는 별칭 단어와 일치합니다. 루프를 피하려면 다음을 수행하십시오.한 번새로운 첫 번째 단어가 확장되는 별칭과 일치하는 경우. 이렇게 하면 루프 없이 작업할 수 있습니다.

    $ alias ls='ls -la'
    $ ls dir               # will expand to `ls -la dir`
    

    참고: 별칭 값의 마지막 문자가 비어 있으면 별칭 뒤의 다음 명령 단어도 별칭 확장을 확인합니다.

  3. (생성된) 첫 번째 단어가 확장(예: a $var)인 경우 대체됩니다(따옴표가 없는 경우 IFS 토큰화(및 글로빙)의 영향을 받음).

    $ var='echo -e '
    $ $var test '\twords'            # will expand to `echo -e test words`
    test    words
    
  4. 위의 확장 이후(선택적 변수 할당 이후) 생성된 첫 번째 단어는 다음 순서로 검색됩니다.

    • 특수 내장 기능(POSIX 모드에서만)
    • 기능
    • 내장 기능
    • 해시 명령(해싱에 대한 도움말 읽기)
    • 외부 명령( $PATH디렉토리에서 검색)

    발견된 첫 번째 일치 항목이 실행됩니다.

테스트를 위해 다음과 같은 방법으로 순서를 변경할 수 있습니다.

  • 별칭을 우회하려면 \test또는 다른 유형의 확장을 사용하세요.
  • 함수와 별칭을 무시하려면 를 사용하세요 command test.
  • 내장된 기능을 실행하려면 를 사용하세요 builtin test.
  • 외부 애플리케이션을 실행하려면 명시적 경로( )를 사용하십시오 /bin/test.

따라서 함수는 다음과 같이 정의됩니다.

$ ls(){ ls; }

무한 루프에서 호출됩니다. 또한 동일한 첫 번째 단어에 대한 스크립트를 호출합니다. 그것은 다음과 같습니다:

#!/bin/bash
$0 "$@"

커널이 충돌할 때까지 동일한 스크립트가 루프에서 다시 실행됩니다(사용 중인 커널에 스크립트를 계속 호출하는 데 제한이 있는 경우).

이 시퀀스는 다음을 실행하여 표시됩니다 type -a.

$ test(){ echo test function; }
$ alias test=test
$ type -a
test is aliased to `test'
test is a function
test ()
{
    echo test function
}
test is a shell builtin
test is /usr/bin/test

질문에 정의된 함수는 별칭만 우회 \vim하지만 함수는 우회하지 않습니다. 별칭과 기능을 우회하려면 명령을 사용하십시오. 이 함수는 필요한 작업을 수행해야 합니다.

vim() {
        if [ -w "$1" ]; then
            command vim "$1"
        else
            sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
        fi
      }

답변3

함수에 vim호출 바이너리에 대한 전체 경로를 제공하면 재귀 루프가 발생하지 않습니다. 또한 -Hsudo 옵션을 사용하여 $HOME을 설정합니다.

vim() {
    if [ -w "$1" ]; then
        command vim "$1"
    else
        sudo env HOME="$HOME" \vim -u ~/.vimrc "$1"
    fi
}

감사해요. 나는 이것을 기대하지 않았지만 이제 나도 .bashrc그것을 가지고 있습니다.


편집하다: 업데이트된 호출 command vim대신 sudo env이렇게 sudo -h하면 계속 머무를 루프가 없습니다.

관련 정보