이 스크립트에서 sed 명령이 수행하는 작업을 이해할 수 없습니다.

이 스크립트에서 sed 명령이 수행하는 작업을 이해할 수 없습니다.

다른 스크립트에서 파생된 기능을 가진 스크립트가 있습니다. 한 줄씩 하려고 했는데 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

  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인수를 사용하지 않으면 실행할 마지막 명령의 종료 코드가 반환되므로 호출자는 성공 또는 실패를 감지할 수 있습니다.

  2. 저자는 셸에서 따옴표의 목적이나 작동 방식, 중괄호(예: )가 무엇인지 이해하지 못하는 것 같습니다 ${var}.아니요인용에 대한 대안은 다음을 "$var"참조하세요 .$VAR 대 ${VAR} 및 인용 여부그리고공백이나 기타 특수 문자 때문에 쉘 스크립트가 멈추는 이유는 무엇입니까?.

    저자는 $2필요할 때 절대로 인용하지 않습니다(파일 이름에는 공백, 탭, 줄 바꿈 및 인용 없이 사용하면 쉘 스크립트를 손상시킬 수 있는 쉘 메타 문자가 포함될 수 있습니다).

  3. 세 가지 if/elif 테스트는 첫 번째 인수( $1)에 =기호, 공백 또는 두 개의 탭이 포함되어 있는지 확인합니다. 발견된 버전에 따라 약간 다른 세 가지 버전의 sed 스크립트 중 하나를 실행합니다.

    sed 스크립트는 ="키" 변형, 공백 또는 두 개의 탭이 있는지 파일을 확인하고 선택적으로 를 사용합니다 #. 일치하는 항목이 있으면 이를 값으로 바꾸고 $1루프를 실행합니다. 나머지 출력 파일을 읽으십시오. 내 생각에 여기서의 목적은 첫 번째 항목만 교체하는 것입니다 key=value.

    일치하는 항목이 없으면(따라서 루프 및 종료가 수행되지 않음) $1파일 끝에 추가됩니다.

  4. 저자는 이것에 대해 너무 많이 생각한 것 같습니다(또는 충분하지 않을 수도 있습니다). $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/}여전히 동일하게 작동할 수 있습니다.

  5. 함수 이름 lineinfile은 매우 일반적이지만 수행하는 작업은 매우 구체적입니다. 더 나쁜 것은 이름이 일치하지 않거나 함수가 실제로 사용되는 용도를 암시조차 하지 않는다는 것입니다. 이는 일반적으로 나쁜 습관으로 간주됩니다.

관련 정보