
Bash 스크립트에는 다음 변수가 있습니다.
file_name='this_is_the_hart_part.csv'
사용
var2=$(echo $file_name | sed -e 's/_{2}\(.*\)_{3}/\1/')
하위 문자열 "the"(변수 $file_name의 밑줄 숫자 2와 3 사이)를 추출하고 싶습니다.
하지만 $file_name과 동일한 $var2를 돌려받습니다. sed 명령을 어떻게 변경해야 합니까?
답변1
에서 지원하는 정규 표현식 유형은 sed
과의 비탐욕적 일치를 허용하지 않습니다 *
.
세 번째로 구분된 필드를 얻고 싶습니다 _
. 다음을 사용하면 가장 쉽습니다 cut
.
cut -d '_' -f 3
또는 다음을 사용하여 awk
:
awk -F '_' '{ print $3 }'
또는 셸에서 처음 두 개의 필드를 연속적으로 제거한 다음 끝 부분을 잘라냅니다.
str=${file_name#*_}
str=${str#*_}
str=${str%%_*}
"$str"
the
마지막에 하는 말이겠지 . 이 마지막 변형을 사용하는 것이 이 세 가지 중에서 가장 빠르고 가장 강력한 방법이 될 것입니다.
변수 대체를 수행하면 첫 번째 밑줄을 포함하여 선행 비트가 제거된 ${variable#*_}
문자열이 생성됩니다 . $variable
는 ${variable%%_*}
첫 번째 밑줄부터 끝까지 모든 것을 제거합니다 $variable
. 이는 표준 변수 대체입니다.
파일 이름에 변수 대체를 사용하면 개행 문자가 포함된 파일 이름에 대처할 수 있다는 이점 awk
이 sed
있습니다 cut
. 일반적으로 파일 이름에는 줄 중심 텍스트 편집 도구를 사용하지 마십시오.
게다가 을(를) 사용하고 있습니다 echo $file_name
. 는 인용되지 않았 으므로 단어 분할( 기본적으로 공백, 탭 및 개행 문자 $file_name
의 일부이기도 한 모든 문자에서)을 거치고 생성된 단어에 파일 이름 글로빙 문자가 포함된 경우 현재 디렉터리의 파일 이름과 일치됩니다. $IFS
껍질로. 그리고 파일 이름의 백슬래시도 사라지거나 원치 않는 효과를 가져올 수 있습니다(확장을 인용하더라도). 쉘 은 또한 인용되지 않은 ksh
값에 대해 중괄호 확장을 수행합니다 .$file_name
답변2
첫 번째 메모 sed
는텍스트기본적으로 한 번에 한 줄씩 작동하는 유틸리티입니다. 파일 이름에는 모든 문자(개행 포함)는 물론 문자가 아닌 문자(문자가 아닐 수도 있음)를 포함할 수 있습니다.텍스트).
또한,변수를 따옴표로 묶지 않은 상태로 두는 것은 매우 특별한 의미를 갖습니다., 당신은 거의 그렇게 하고 싶지 않을 것입니다.잠재적으로 매우 위험함.
또한,echo
임의의 데이터를 출력하는 데 사용할 수 없습니다 . printf
대신 사용하세요..
또한 Bourne과 유사한 쉘의 변수 할당 구문은 var=value
, 가 아닙니다 $var=value
.
다음을 사용하여 의 전체 출력 echo
(또는 더 나은 )을 의 패턴 공간 printf
에 로드할 수 있습니다.sed
printf '%s\n' "$filename" | sed -e :1 -e '$!{N;b1' -e '}'
그런 다음 두 번째와 세 번째 사이의 부분을 추출하는 코드를 추가할 수 있습니다 _
.
var2=$(
printf '%s\n' "$filename" |
sed -ne :1 -e '$!{N;b1' -e '}' -e 's/^\([^_]*_\)\{2\}\([^_]*\)_.*/\2/p'
)
탐욕스럽지 않은 부분은 [^_]*
(비문자의 시퀀스 )을 사용하여 해결됩니다. 이는 우리가 과거 경계와 일치하지 않는다는 보장 _
과는 반대로 (비록 많은 구현에서 여전히 비문자로 인해 질식하게 됩니다)..*
_
이 경우 대신 쉘 매개변수 확장 연산자를 사용할 수 있습니다.
case $filename in
(*_*_*_*) var2=${filename#*_*_}; var2=${var2%%_*};;
(*) var2=;;
esac
파일 이름이 텍스트가 아니거나 추출하려는 부분이 개행 문자로 끝나는 경우 더 잘 작동합니다(또한 더 효율적입니다).
일부 쉘에는 다음과 같은 고급 연산자가 zsh
있거나 ksh93
다음과 같은 고급 연산자가 있습니다.
zsh
:분할하여
_
세 번째 필드를 얻습니다.var2=${"${(@s:_:)filename}"[3]}
및 역참조 사용
${var/pattern/replacement}
(이 경우 변수에 밑줄이 3개 이상 포함되어 있는지 또는 대체 항목이 없는지 먼저 확인해야 합니다).set -o extendedglob var2=${filename/(#b)*_*_(*)_*/$match[1]}
ksh93
:var2=${filename/*_*_@(*)_*/\1}
답변3
sed
@Kusalananda는 잘못된 도구이며 탐욕스럽지 않은 일치를 수행할 수 없다는 것이 맞습니다 . 그러나 탐욕스럽지 않은 [^_]*
일치 에 대한 해결 방법을 사용할 수 있습니다.
_
따라서 귀하의 경우 다음과 같이 할 수 있습니다.
printf '%s\n' "$file_name" | sed -e 's/^[^_]*_[^_]*_\([^_]*\).*$/\1/g'
하지만... 귀하의 사용 사례에는 다른 도구를 사용하는 것이 더 좋습니다...