다음 명령을 가정
search /home/user proc .h .c .txt ...
find
주어진 이름으로 시작하고 주어진 확장자 중 하나로 끝나는 모든 파일을 가져오는 명령을 사용하여 스크립트를 작성 중입니다 .
루프를 사용하여 성공적으로 구축했습니다.
directory=$1
fileName=$2
fileExtensions=""
for arg in "$@"
do
#skipping first and second argument
if [ $arg = $1 -o $arg = $2 ]; then
continue;
fi
fileExtensions+="${arg//.}\|"
done
#removing the last '|', otherwise regex parser error occurs
fileExtensions=${fileExtensions::-1}
find $directory -name "$fileName*" -regex ".*\.\($fileExtensions)"
정규식을 사용하여 이를 달성하는 더 우아한 방법이 있습니까?
당신의 도움을 주셔서 감사합니다!
답변1
스크립트는 다음과 같이 단순화될 수 있습니다.
directory=$1
fileName=$2
shift 2
a="$*" b="${*#.}"
(( ${#a} - ${#b} - $# )) && echo "some extension(s) is(are) missing a leading dot." >&2
fileExtensions="$(IFS=\|; echo "${*#.}")"
find "$directory" -name "$fileName*" -regextype posix-egrep -regex ".*\.($fileExtensions)$"
기본적으로 find는 emacs 유형의 정규식을 허용하며 해당 유형을 사용하려면 몇 가지 백슬래시가 필요합니다 \|
(예: 위에서 언급한 것처럼). 이는 다른 유형의 정규식을 사용하면 피할 수 있습니다.
선행 점(있는 경우)을 제거 하고 나머지 모든 "위치 인수"를 서브쉘 실행에 ${*#.}
사용되도록 설정된 IFS의 첫 번째 문자 값과 연결합니다 .|
변수 할당과 "매개변수 확장"만으로 충분합니다.
편집하다(( ${#a} - ${#b} - $# ))
매개변수 목록에 제공된 모든 확장자가 점으로 시작하는지 확인하는 데 사용됩니다 .
${#a}
연결의 모든 매개변수의 문자 수( )( a=$*
)는 연결의 모든 매개변수의 문자 수 ( )(앞의 점 하나 제거됨)( ) 에 매개변수 수( )를 더한
값과 같아야 합니다 .${#b}
b=${*#.}
$#
${#a} == ${#b} + $#
모든 인수에 앞에 점이 있는 경우.
산술 테스트:
(( ${#a} - (${#b} + $#) ))
또는:
(( ${#a} - ${#b} - $# )) && echo "missing leading dot(s)."
편집 2
명령줄 인수에 제공된 확장 목록은 여기에서 처리됩니다.
fileExtensions="$(IFS=\|; echo "${*#.}")"
내부 및 외부 작동 방식은 다음과 같습니다.
$* # This generates a string of all positional arguments using IFS.
에서 LESS=+'/Special Parameters' man bash
:
즉, "$*"는 "$1c$2c..."와 동일합니다. 여기서 c는 IFS 변수 값의 첫 번째 문자입니다.
그런 다음 "매개변수 확장"을 사용하여 각 위치 매개변수의 앞 부분을 잘라냅니다.
${parameter#word} # used as ${*# } above.
에서 LESS=+/'parameter#word' man bash
:
인수가 @ 또는 *인 경우 패턴 제거 작업이 각 위치 인수에 차례로 적용되고 확장이 결과 목록이 됩니다.
word
이전 확장의 점으로 설정되어 .
모든 위치 매개변수 앞의 점이 제거되었습니다.
이때 IFS는 |
문자로 시작하므로 해당 문자는 문자열을 구성하는 데 사용되며 |
앞에 점이 제거된 매개변수 목록의 구분 기호 역할을 합니다.
이 문자열은 인쇄하기 위해 echo 명령에 제공됩니다.
그러나 echo 명령을 실행하기 전에 변수 $IFS
는 |
.
이는 명령 실행으로 래핑됩니다 $(…)
(종료 시 IFS에 대한 변경 사항을 잊어버리는 하위 쉘 생성).
그런 다음 문자열을 변수에 할당합니다.
fileExtensions="$(IFS=\|; echo "${*#.}")"
즉, .c .h .txt
로 변환합니다 c|h|txt
.
답변2
정규 표현식이 작동해야 하는 것 같고, 제가 생각할 수 있는 유일한 다른 옵션은 비슷한 것인데 -name "proc*" \( -name "*.c" -o -name "*.h" ... \)
그렇게 간단하지 않을 수도 있습니다.
당신이 할 수 있는 몇 가지 작은 일들:
shift
1) 루프 내부의 조건을 대체하기 위해 및 를 사용할 수 있습니다 .$1
$2
2) 결합하여 -name
결국 -regex
에는 얻을 수 있습니다.-regex ".*/proc.*\.(c|h|txt)"
3) 코드를 더 짧게 만들고 싶다면 IFS
위치 매개변수를 사용하여 연결할 수 있습니다 $*
.
$ set .c .h
$ IFS='|'
$ echo "(${*/./})"
(c|h)
(가독성이나 우아함을 의미하는 것은 아닙니다.)