Linux 찾기 명령이 디렉토리를 건너뜁니다.

Linux 찾기 명령이 디렉토리를 건너뜁니다.

디렉토리 에 특정 네트워크 공유 폴더가 마운트되어 있습니다 /media.

sudo find / -name foo나는 그런 일을 할 때 항상 디렉터리를 건너뛰어야 하는지 확인하고 싶습니다 /media.

명령 에 매개 변수를 전달하고 싶지 않습니다 . 항상 기본적으로 디렉터리를 건너뛰는 find방식으로 시스템을 구성하고 싶습니다 .find/media

답변1

이 상황에서는 고려해야 할 여러 가지 극단적인 경우가 있습니다. 첫 번째 접근 방식은 find / -path '/media' -prune -o ...검색 경로가 절대적이고 로 시작하는 경우에만 충분합니다 /. 시나리오는 절 cd / && find * ...과 절대 일치하지 않습니다 -path '/media'.

다행히 매개 -inum변수가 도움이 될 수 있습니다. Inode 번호는 마운트된 파일 시스템마다 고유하므로 제외하려면 /media파일 시스템과 inode 번호로 구성된 튜플을 식별해야 합니다.

다음 (긴) 스크립트는 제외되며 /media, 유용한 엣지 케이스를 충분히 포착할 수 있기를 바랍니다.


#!/bin/bash
#
FIND=/usr/bin/find


# Process prefix arguments
#
opt_H= opt_L= opt_P= opt_D= opt_O=
while getopts 'HLPD:O:' opt
do
    case "$opt" in
        H)      opt_H=-H ;;
        L)      opt_L=-L ;;
        P)      opt_P=-P ;;
        D)      opt_D="-D $OPTARG" ;;
        O)      opt_O="-O $OPTARG" ;;
    esac
done
shift $((OPTIND - 1))


# Find the inode number for /media and its filesystem
#
m_inode=$(stat -c '%i' /media 2>/dev/null)
m_fsys=$(stat -c '%m' /media 2>/dev/null)


# Collect the one or more filesystem roots to search
#
roots=()
while [[ 0 -lt $# && "$1" != -* ]]
do
    roots+=("$1")
    shift
done


# Collect the "find" qualifiers. Some of them need to be at the front
# of the list. Unfortunately.
#
pre_args=() args=()
while [[ 0 -lt $# ]]
do
    # We really ought to list all qualifiers here, but I got tired of
    # typing for an example
    #
    case "$1" in
        -maxdepth)      pre_args+=("$1"); pre_args+=("$2"); shift 2 ;;
        -mindepth)      pre_args+=("$1"); pre_args+=("$2"); shift 2 ;;
        -mount|-xdev)   pre_args+=("$1"); shift ;;
        -depth|-d)      pre_args+=("$1"); shift ;;
        -name|-iname)   args+=("$1"); args+=("$2"); shift 2 ;;
        -path|-ipath)   args+=("$1"); args+=("$2"); shift 2 ;;
        *)              args+=("$1") ; shift ;;
    esac
done
test -z "${args[*]}" && args=('-print')


# Iterate across the collected filesystem roots, attempting to skip
# /media only if the filesystem matches
#
exit_ss=0
for root in "${roots[@]}"
do
    fsys=$(stat -c '%m' "$root" 2>/dev/null)
    if [[ -n "$m_inode" && -n "$m_fsys" && "$fsys" == "$m_fsys" ]]
    then
        # Same filesystem. Exclude /media by inode
        #
        "$FIND" ${opt_H:+"$opt_H"} ${opt_L:+"$opt_L"} \
                ${opt_P:+"$opt_P"} ${opt_O:+"$opt_O"} \
                ${opt_O:+"$opt_O"} "$root" "${pre_args[@]}" \
                \( -inum "$m_inode" -prune \) -o \( "${args[@]}" \)
        ss=$?
        [[ 0 -lt $ss ]] && exit_ss="$ss"
    else
        # Different filesystem so we don't need to worry about /media
        #
        "$FIND" ${opt_H:+"$opt_H"} ${opt_L:+"$opt_L"} \
                ${opt_P:+"$opt_P"} ${opt_O:+"$opt_O"} \
                ${opt_O:+"$opt_O"} "$root" "${pre_args[@]}" \
                "${pre_args[@]}" \( "${args[@]}" \)
        ss=$?
        [[ 0 -lt $ss ]] && exit_ss="$ss"
    fi
done


# All done
#
exit $exit_ss

답변2

find"간단한" 사용법 (예: 여러 디렉토리가 아닌 옵션 없음 ) 을 고수 -H -L -D -P -O하고 옵션을 사용해도 괜찮다 면 -xdev이 간단한 대답을 시도해 보십시오. 이는 마운트된 모든 파일 시스템을 제외합니다(예: $HOME별도로 마운트된 경우에도 마찬가지).

find다른 파일 시스템에 적합하지 않도록 bash 기능을 정의할 수 있습니다 . 이것을 당신의 안에 넣으십시오 ~/.bashrc(당신이 사용하고 있다고 가정 bash)

find () {
  local path="${1}"
  shift
  command find "${path}" -xdev "${@}"
}

설명: 별칭 대신 함수를 사용해야 합니다. 왜냐하면 find인수 순서가 매우 까다롭기 때문입니다. 은 path첫 번째 인수여야 합니다. 따라서 path지역 변수에 저장하고 인수 목록( shift)에서 팝합니다. 그런 다음 command find경로와 나머지 모든 인수를 사용하여 원래 찾기를 실행합니다 $@. 앞부분 commandfind재귀 호출로 끝나지 않도록 해줍니다.

~/.bashrc파일을 사용하려면 먼저 소스를 가져와야 합니다.

source ~/.bashrc

그런 다음 새 버전의 find. 다음을 사용하여 언제든지 정의를 확인할 수 있습니다.

> type find
find is a function
find ()
{
    local path="${1}";
    shift;
    command find "${path}" -xdev "${@}"
}

답변3

(   set -e -- "$(command -v find)"
    [ -x "${1:?}"  ]
    [ ! -e "$1cmd" ]
    [ ! -L "$1cmd" ]
    mv -- "$1"  "$1cmd"
    cat > "$1"
    chmod +x -- "$1"
)   <<""
#!/bin/sh -f
eval '  exec    "$0cmd" '"${1$(                       # f!'"ing colors
        unset   i L O M rt IFS
        chk()   case   ${O+$2}${2--}    in            # $O must be set or $2
                (-maxdepth"$2") M=      ;;            # unset to match "$2$2"
                ([\(!]"$2"|-*"$2")                    # this is the last match
                        printf  %s${1+%b}%.d\
                               "$rt" \\c 2>&-   &&    # printf fails if ! $1  
                        chk(){  ${1+:} exit; }  ;;    # chk() = !!$1 || exit
                (-?*)   shift   $((OPTIND=1))         # handle -[HLP] for 
                        while   getopts :HLP    O     # path resolution w/
                        do      case    $O${L=} in    # NU$L expansions
                                (P)     unset L ;;    # $[HL]=:- $P=-
                                (\?)    rt= chk ''    # opt unexpected  &&
                                        return  ;;    # abandon parse
                        esac;   done;   unset O ;;    # $O is unset until 
                (${M-${O=?*}})                        # above matches fail
                      ! [ ! -L "${L-$2}" ]      ||    # ! -P ||!! -L ||
                        [ !  / -ef "$2"  ]      ||    # !  / == $2   ||
                        rt=$rt' ! \( -path "${'$i'%/}/media/*" -prune \)'
                esac
        while   chk ${1+$((i+=1)) "$1"}               # loop while args remain
        do      printf ' "${'$i}\"                    # printf args to eval
                shift                                 # shift args away
        done                                          # done
)"

경로 인수 중 하나라도 동일한지 확인하는 find것을 금지하기 위해 몇 가지 인수를 삽입하는 래퍼 스크립트가 있습니다 .find/media//

위 스크립트에는 두 부분이 있습니다. 실제 스크립트가 있습니다.(이것은 다음의 모든 것입니다<<""\n) 상단에 1회 실행 설치 비트가 있습니다.((첫 번째 일치하는 괄호 쌍 사이의 모든 것 )).

설치하다

(   set -e -- "$(command -v find)"         #get /path/to/find
    [ -x "${1:?}"  ]                       #else loudly fail
    [ ! -e "$1cmd" ]                       #fail if /path/to/findcmd
    [ ! -L "$1cmd" ]                       #and double-check
    mv -- "$1"  "$1cmd"                    #rename .../find -> .../findcmd
    cat > "$1"                             #copy stdin to .../find
    chmod +x -- "$1"                       #set new find's executable bit
)   <<"" ###stdin

설치 부분은 약간의 주의가 필요합니다~ 아니다시스템에서 아무것도 직접 수정하지 않고도 성공적으로 완료할 수 있지만 $PATH'd 실행 파일의 파일 이름은 에서 find으로 변경하려고 하며 이미 존재하지 않는 경우 시도합니다 . 테스트가 사실인 것으로 판명되고 명령을 적용할 수 있는 적절한 권한이 있는 경우 실행 파일의 이름이 바뀌고 그 자리에 이름이 지정된 새 셸 스크립트가 설치됩니다 ./path/to/find/path/to/findcmd/path/to/findcmdfindfind

설치된 스크립트는 그 후 영원히 findcmd남아 있는 이름이 변경된 실행 파일 에 의존합니다.(따라서 사용하는 경우 패키지 관리자에게 이를 알리고 싶을 것입니다)$0cmd그리고 호출될 때마다 모든 인수를 살펴본 후 호출 됨으로 대체됩니다 . 설치를 영구적으로 만드는 데 필요한 모든 작업을 수행하지 않으면 결국 대부분의 패키지 관리자는 설치된 스크립트를 find어느 시점에서 새로 업데이트된 바이너리로 덮어쓰게 될 것이므로 다음을 제외하고는 시작한 곳으로 바로 돌아갈 수 있습니다. 또한 시스템 디렉토리에 이전 find이름이 지정됩니다 .findcmd../bin

적절한 권한이 부여되고 시스템이 과도한 놀라움을 보장하지 않는 경우 전체 스크립트는 쉘 프롬프트에 복사하여 붙여넣는 방식으로 자체 설치 가능해야 합니다.(마지막에 추가 RETURN을 수행해야 하지만). 그런 식으로 작동하지 않는다면 적어도 그 시도로 인해 해를 끼치는 일은 없어야 합니다.

새로 발견하다

#!/bin/sh -f
eval '  exec    "$0cmd" '"${1+$(                      # f!'"ing colors
        unset   i L O M rt IFS
        chk()   case   ${O+$2}${2--}    in            # $O must be set or $2
                (-maxdepth"$2") M=      ;;            # unset to match "$2$2"
                ([\(!]"$2"|-*"$2")                    # this is the last match
                        printf  %s${1+%b}%.d\
                               "$rt" \\c 2>&-   &&    # printf fails if ! $1  
                        chk(){  ${1+:} exit; }  ;;    # chk() = !!$1 || exit
                (-?*)   shift   $((OPTIND=1))         # handle -[HLP] for 
                        while   getopts :HLP    O     # path resolution w/
                        do      case    $O${L=} in    # NU$L expansions
                                (P)     unset L ;;    # $[HL]=:- $P=-
                                (\?)    rt= chk ''    # opt unexpected  &&
                                        return  ;;    # abandon parse
                        esac;   done;   unset O ;;    # $O is unset until 
                (${M-${O=?*}})                        # above matches fail
                      ! [ ! -L "${L-$2}" ]      ||    # ! -P ||!! -L ||
                        [ !  / -ef "$2"  ]      ||    # !  / == $2   ||
                        rt=$rt' ! \( -path "${'$i'%/}/media/*" -prune \)'
                esac
        while   chk ${1+$((i+=1)) "$1"}               # loop while args remain
        do      printf ' "${'$i}\"                    # printf args to eval
                shift                                 # shift args away
        done                                          # done
)}"

래퍼 스크립트를 작성할 때 가장 먼저 따르는 규칙은 다음과 같습니다.손을 떼다. 프로그램이 필요하면 하나 작성하려고 하지만 이미 포장할 가치가 있는 프로그램이 있으므로 프로그램이 이미 수행하는 작업을 방해 없이 수행하도록 하고 달성하기 위한 동작에 대해 가능한 한 적은 수정을 가하도록 노력할 것입니다. 내 최종 목표. 이는 랩의 목적과 직접적으로 관련되지 않는 방식으로 실행 환경에 영향을 미칠 수 있는 작업을 수행해서는 안 된다는 것을 의미합니다. 따라서 나는 변수를 설정하지 않고 인수를 해석하지 않으며 I/O 스트림을 건드리지 않으며 래핑된 프로그램의 프로세스 그룹이나 상위 PID를 변경하지 않습니다. 모든 면에서 래퍼는 가능한 한 일시적이고 투명해야 합니다.

위의 스크립트는 이 목표를 이전보다 더 많이 달성합니다. 이전에는 특히 경로 해결과 관련하여 만족스럽지 않았지만 그 문제를 해결했다고 믿습니다. 이를 제대로 수행하려면 또는 옵션 중 하나가 유효하고 이를 부정하지 않은 경우 [HLP]와 기호 링크를 올바르게 비교할 수 있도록 상태를 추적해야 했습니다. 링크 테스트가 통과하면 현재 인수에서 동일한 파일 inode가 일치하는지 확인됩니다 . 즉, 사실상 모든 이름이 작동함 을 의미합니다./-H-L-P-ef//-H(효과적 이거나 -L효과적인 경우 심볼릭 링크를 포함하기 위해 ). 그래서 그런 것에 대해 기분이 좋아졌고, 기본적으로 검색을 차단 /proc하고 검색 /sys하지 /dev못하도록 설정했습니다 ./

특히 좋은 점은 호출된 상태를 에 전달하기 전에 수정하지 않는 것입니다 $0cmd. 처리할 준비가 되지 않은 옵션이 포함된 인수 세트를 해석하는 것을 거부하고 이러한 경우 전체 세트를 그대로 전달하므로 $0cmd경로 검색을 차단하지 않을 수도 있고 영향을 미치지도 않습니다. find다른 방식으로의 행동. 그렇기 때문에 eval "exec wrapped_program $(arg-handler)"제가 이런 종류의 작업에 가장 선호하는 방법은 바로 이 방법입니다.

최상위

실제로 위에서 지적한 바와 같이, 최상위 수준에서 전체 쉘 스크립트는 하나의 간단한 명령에 불과합니다. 이 명령은 자신을 다른 실행 파일로 바꾸라고 지시합니다. 수행되는 모든 작업은 $(명령 대체 )하위 셸 내에서 수행되며 수정 여부에 관계없이 모든 상태가 완전히 현지화됩니다. 그 뒤에 있는 목적은 eval실제로 불필요하게 영향을 미칠 필요 없이 스크립트의 인수를 다시 살펴보는 것입니다. 이것이 바로 이 래퍼의 목적입니다.

$(sub 명령이 )작업을 완료 하면 결과 exec'd 명령은 다음 중 하나가 됩니다.

exec "$0cmd" "${1}" ... ! \( -path "${[num]%/}/media/*" -prune \) "${2}" ...

...모든 원래 인수(있는 경우)는 원래의 변경되지 않은 형식으로 순서대로 번호별로 참조됩니다.(null 인수도 포함)여섯 가지 외에도( !, \(, -path, "${[num]%/}/media/*", -prune, \))/ -ef "${num}"인수 스캔 중 성공적인 각 테스트에 대해 발생하는 삽입 세트입니다 . 그렇지 않으면 간단히 다음과 같습니다.

exec "$0cmd" "${1}" "${2}" "${3}" "${4}" ...

...여기서 모든 원래 인수는 전혀 삽입하지 않고 동일한 방식으로 참조됩니다.

따라서 이 래퍼가 래핑된 대상의 환경에 대해 수행할 수 있는 유일한 두 가지 수정은 다음과 같습니다.

  • 프로세스 이름을 자체 이름에서 자체 이름으로 변경합니다. +cmd. 이런 일은 항상 발생합니다.

  • 호출된 항목 목록에 루트 일치 항목당 6개의 인수를 삽입할 수 있습니다.

첫 번째 변경이 허용 가능한 것으로 제공되는 한(피할 수는 있지만), 여기에 동작 수정과 관련하여 단일 실패 지점이 있습니다. 이는 인수 삽입이 유효한지 여부입니다. 호출과 관련된 모든 오류는 단순히 범위를 벗어나며 랩 대상에 의해 처리되어야 하며 이 래퍼는 해당 작업에 신경을 쓰려고 합니다.

인수 처리기

명령 대체 내에서 문자열 ""이 가장 좋은 방법이기 때문에 먼저 설정 해제할 변수를 초기화합니다.~ 아니다필요할 때 경로를 일치시킵니다. 그런 다음 함수를 선언 하고 나중에 각 스크립트의 호출 인수에 대해 1씩 증가하는 루프 chk()의 각 반복에 대해 함수를 호출합니다 . 공백과 달러 기호 앞에 따옴표와 중괄호로 묶인 각 증분을 명령 하위의 표준 출력에 인쇄합니다 .while$i$i

printf ' "${'$i}\"

...

 "${1}"

chk()반복마다 인수의 복사본을 가져오는 호출을 반복한 다음 shift아무것도 남지 않고 루프가 완료될 때까지 처리합니다. chk()인수를 패턴과 비교하고 적절한 조치를 취합니다.

  • (-maxdepth"$2") M= ;;

    • 이 설정 되면 $M마지막 패턴은 해당 블록의 후속 경로 비교 테스트에만 실패할 수 있는 null 문자열과만 일치할 수 있습니다.rt=$rt+!\(그런 경우에는 절대 발생하지 않습니다. 그렇지 않으면 아무 일도 일어나지 않습니다.

    • POSIX 사양은 피연산자 -[HL]이전에만 인식되어야 하며 [...path...]다른 피연산자는 지정되지 않습니다. [...path...]피연산자와 테스트 피연산자에 대한 설명은 다음과 같습니다 .

      첫 번째 피연산자와 a로 시작하거나 a 또는 a 인 첫 번째 피연산자를 포함하지 않는 후속 피연산자는 피연산자 로 해석됩니다 . 첫 번째 피연산자가 a 로 시작하거나 a 또는 a 인 경우 동작은 지정되지 않습니다. 각 경로 피연산자는 파일 계층 구조의 시작점에 대한 경로 이름입니다.!([...path...]!(

  • ([\(!]"$2"|-*"$2")

    • 현재 인수는 단일 (왼쪽 괄호 또는 !강타이거나 대시로 시작 -*하지만 대시가 아니며 -maxdepth마지막 패턴이 적어도 한 번 일치했습니다.

    • printf %s ${1+%b}%.d "$rt" \\c 2>&- &&

      • 의 값 $rt(있는 경우)을 명령 대체의 표준 출력에 쓴 다음 이스케이프의 길이가 0인 쓰기에 성공하거나 , 설정되지 않고 인수의 끝이 있는 경우 동일한 길이의 ecimal \c %b로의 실패한 변환이 이어집니다. 도달했다. 이 실패로 인해 루프가 종료됩니다.%.d$1while
    • chk(){ ${1+:} exit; }

      • printf성공하면 인수 chk()를 수정하려고 시도한 것뿐입니다. 이 시점부터 루프는 while나머지 인수를 계속 처리하고 인쇄할 수 있지만, chk()이 모든 인수가 소진될 때까지 아무 작업도 수행하지 않으며 그 시점에서는 exit서브셸만 수행됩니다. 따라서 두 번째 패턴이 한 번이라도 일치하면 다른 패턴은 다시 일치하지 않습니다.
  • (-?*)

    • 현재 인수는 2자 이상이며 대시로 시작됩니다. 이 패턴은-*"$2"위의 패턴 보다 배타적 $O이며 한 번 설정되면 최소한 단일 인수가 나올 때까지만 일치할 수 있습니다.그렇지 않다일치시키세요. 이러한 방식으로 모든 초기 옵션은 으로 분할되어 getopts일치됩니다 [HPL]. 초기 옵션이 해당 패턴에 맞지 않으면 함수는 자신을 재귀적으로 호출하여 위의 패턴과 일치시키고 재정의합니다 chk(). 이러한 방식으로 명시적으로 처리되지 않은 모든 인수 시퀀스는 그대로 전달되고 findcmd결과에 대해 원하는 모든 작업을 수행합니다.

    • -[HL]플래그 변수 와 일치하는 각 초기 옵션에 대해 $L널 문자열로 설정됩니다. 그리고 일치하는 각각의 항목 -P $L은 입니다 unset.

  • (${M-${O=?*}})

    • 일치하지 않는 첫 번째 인수는 의 패턴 설정을 트리거 -?*합니다 . 그 이후에는 처음 두 패턴 중 어느 것이든 일치할 수 있습니다 . 일치하고 null 문자열로 설정된 경우 이 패턴은 null이 아닌 다른 인수와 다시 일치할 수 없으며 두 번째 패턴의 단일 일치만 있으면 일치하려는 모든 시도를 중단할 수 있습니다.$O?*${O+$2}${2--}-maxdepth$2M=

    • -[HLP]첫 번째 옵션 시퀀스 뒤, 다른 인수 -*또는 이 패턴과 일치하는 인수 앞에 발생하는 null이 아닌 인수 [\(?!]는 경로 확인을 위해 테스트됩니다. $L가 설정되지 않은 경우 심볼릭 링크이거나 잘못된 경로 이름이면 ! ! -L "${L-$2}"테스트가 통과되지만 , 그렇지 않으면 경로 이름이 nullstring과 일치할 수 없기 때문에 항상 실패합니다.$2${L=}

    • 이전 테스트에 실패한 인수만 !부정된 inode 일치 여부를 확인 /하고 두 테스트 모두에 실패한 인수는 두 번째 패턴이 일치하거나 인수 끝에 도달할 때까지 기록되지 않는 문자열 $rt과 함께 자체 설정됩니다 . ! \( -path "${[num]%/}/media/* -prune \)먼저 온다.

관련 정보