Bash 스크립트에는 다음 변수가 있습니다.
file_name='this_is_the_hart_part.csv'
사용
var2=$(echo $file_name | sed -e 's/_{2}\(.*\)_{3}/\1/')
하위 문자열 "the"(변수 $file_name에서 2와 3 사이의 밑줄이 그어진 숫자)를 추출하고 싶습니다.
하지만 $var2는 $file_name과 같습니다. 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
파일 이름에 변수 대체를 사용하면 개행 문자가 포함된 파일 이름을 처리할 수 있지만 or nor 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
@Kusalananda가 맞습니다. sed
잘못된 도구이므로 탐욕스럽지 않은 매칭을 수행할 수 없습니다. 그러나 탐욕스럽지 않은 [^_]*
일치 에 대한 해결 방법을 사용할 수 있습니다.
_
따라서 귀하의 경우에는 다음과 같이 할 수 있습니다.
printf '%s\n' "$file_name" | sed -e 's/^[^_]*_[^_]*_\([^_]*\).*$/\1/g'
하지만... 귀하의 사용 사례에 대해서는 다른 도구를 사용하는 것이 더 나을 것입니다...