다른 스크립트에서 파생된 기능을 가진 스크립트가 있습니다. 한 줄씩 하려고 했는데 sed 정규식이 너무 복잡합니다.
#!/usr/bin/env bash
# This function will update the value associated with a key,
# remove a comment from the beginning of the line,
# or append the key value pair to the end of the file if the key is not found
# To use this function in a script
# source this script:
# . lineinfile
#
# To invoke the function:
# lineinfile "key=value" "filename"
# OR
# lineinfile "key value" "filename"
lineinfile() {
[ -s $2 ] || echo "${1}" >> ${2}
if [[ "$1" == *"="* ]]; then
sed -i -e "/^#\?.*\(${1%%=*}\).*/{s@@${1}@;:a;n;ba;q}" -e "\$a${1}" ${2}
elif [[ "$1" == *" "* ]]; then
sed -i -e "/^#\?.*\(${1%% *}\).*/{s@@${1}@;:a;n;ba;q}" -e "\$a${1}" ${2}
elif [[ "$1" == *$'\t\t'* ]]; then
sed -i -e "/^#\?.*\(${1%%$'\t\t'*}\).*/{s@@${1}@;:a;n;ba;q}" -e "\$a${1}" ${2}
fi
}
함수의 첫 번째 줄 [ -s $2 ] || echo "${1}" >> ${2}
- 두 번째 위치 인수가 크기가 0이 아닌 기존 파일인지 확인한 다음 내용을 $1
파일 끝에 추가합니다. 여기서는 $2
왜 사용됩니까 ?||
if-elif 블록이 무엇을 테스트하고 있는지 잘 모르겠습니다. if 조건에서 와 일치시키려는 것은 무엇입니까 *"="*
*" "*
? *$'\t\t'*
또한 sed 명령이 무엇을 하는지 전혀 모릅니다. 정규 표현식은 복잡합니다. 누구든지 나를 위해 sed 명령을 분석할 수 있습니까?
답변1
||
이전 명령에 0이 아닌 종료 코드(false/error)가 있는 경우 다음 명령이 실행됩니다.[ -s $2 ] || echo "${1}" >> ${2}
다음과 동일:
if ! [ -s $2 ] ; then echo "${1}" >> ${2} ; fi
파일이 존재하지 않거나 비어 있는 경우 이들 중 하나는
$1
첫 번째 인수( )를 파일( )에 추가합니다 .$2
그런데 인용에 대해서는 아래의 다음 항목 2를 참조하세요.그런데 함수는 이 시점에서 반환할 수 있고 반환해야 합니다.
sed
이제 필요한 값이 포함되어 있으므로 파일을 실행할 필요가 없습니다 . 예를 들어 (printf
대신 사용echo
- 참조왜 printf가 echo보다 나은가요?):[ -s "$2" ] || printf '%s\n' "$1" >> "$2" && return
또는 더 나은:
if ! [ -s "$2" ] ; then printf '%s\n' "$1" >> "$2" ; return ; fi
첫 번째 형식의 경우
return
이전 명령(printf
)이 성공한 경우에만 실행됩니다.printf
두 번째 형식은 성공 또는 실패에 관계없이 항상 반환됩니다.return
종속성이printf
후속 항목 에 종속되는 타당한 이유는 없습니다 (이 약식 if/then/fi 구조에서 명령을 "연결"하는 일반적인 관용어일 뿐입니다). 대부분의 경우printf
성공하지만 때로는(예: 권한 또는 디스크가 가득 찬 경우) 실패합니다. 실패하면 함수는~해야 한다어떻게 돌아가든sed
스크립트도 실패하므로 실행할 필요가 없습니다. 그런데return
인수를 사용하지 않으면 실행할 마지막 명령의 종료 코드가 반환되므로 호출자는 성공 또는 실패를 감지할 수 있습니다.저자는 셸에서 따옴표의 목적이나 작동 방식, 중괄호(예: )가 무엇인지 이해하지 못하는 것 같습니다
${var}
.아니요인용에 대한 대안은 다음을"$var"
참조하세요 .$VAR 대 ${VAR} 및 인용 여부그리고공백이나 기타 특수 문자 때문에 쉘 스크립트가 멈추는 이유는 무엇입니까?.저자는
$2
필요할 때 절대로 인용하지 않습니다(파일 이름에는 공백, 탭, 줄 바꿈 및 인용 없이 사용하면 쉘 스크립트를 손상시킬 수 있는 쉘 메타 문자가 포함될 수 있습니다).세 가지 if/elif 테스트는 첫 번째 인수(
$1
)에=
기호, 공백 또는 두 개의 탭이 포함되어 있는지 확인합니다. 발견된 버전에 따라 약간 다른 세 가지 버전의 sed 스크립트 중 하나를 실행합니다.sed 스크립트는
=
"키" 변형, 공백 또는 두 개의 탭이 있는지 파일을 확인하고 선택적으로 를 사용합니다#
. 일치하는 항목이 있으면 이를 값으로 바꾸고$1
루프를 실행합니다. 나머지 출력 파일을 읽으십시오. 내 생각에 여기서의 목적은 첫 번째 항목만 교체하는 것입니다key=value
.일치하는 항목이 없으면(따라서 루프 및 종료가 수행되지 않음)
$1
파일 끝에 추가됩니다.저자는 이것에 대해 너무 많이 생각한 것 같습니다(또는 충분하지 않을 수도 있습니다).
$1
먼저 키와 값 변수로 분리한 경우 하나의 sed 스크립트로 이 작업을 수행할 수 있습니다. 즉, 먼저 데이터를 추출하고 이를$1
일관된 형식으로 "정규화"한 다음sed
이를 스크립트에서 사용합니다.아니면 그냥 Perl로 전체를 다시 작성하세요. 이는 수행하려는 작업에 쉘과 sed의 이점이 필요할 때(그리고 대부분의 sed 버전보다 더 좋고 강력한 정규식 엔진이 있는 경우) 좋은 선택입니다.
if/elif/elif/fi
예를 들어 함수의 일부를 다음으로 바꿉니다.perl -0777 -i -pe ' BEGIN { $r = shift; ($key,$val) = split /(=| |\t\t)/, $r }; s/\z/$r\n/ unless (s/^#?.*\b$key\b.*$/$r/m)' key=value filename
이 Perl 버전은 세 가지 변형(=, 공백, 두 개의 탭 - 마지막 두 개는 인용 필요)에 모두 작동합니다. 전체 파일을 한 번에 삼키고(
-0777
옵션) 여러 줄 검색 및 바꾸기 작업(/m
정규식 수정자)을 수행하려고 시도합니다. 작업이 실패하면 첫 번째 인수(개행 문자 포함)가 파일 끝에 추가됩니다(\z
). 또한 원본에서 ,foo=123
및 를 구별할 수 없는 버그를 수정합니다foobar=123
.\b
이를 위해 단어 경계 표시가 사용됩니다. sed에서는 키 패턴을 사용\<
하고 둘러쌀 수 있습니다 .\>
그건 그렇고, 그
X unless Y
구성은 단지 입니다if not Y, then do X
. 로 작성해도if (! s/^#?.*$key.*$/$r/m) {s/\z/$r\n/}
여전히 동일하게 작동할 수 있습니다.함수 이름
lineinfile
은 매우 일반적이지만 수행하는 작업은 매우 구체적입니다. 더 나쁜 것은 이름이 일치하지 않거나 함수가 실제로 사용되는 용도를 암시조차 하지 않는다는 것입니다. 이는 일반적으로 나쁜 습관으로 간주됩니다.