기본(접미사 제외) 파일 이름의 마지막 3자를 추출하는 가장 짧은 방법

기본(접미사 제외) 파일 이름의 마지막 3자를 추출하는 가장 짧은 방법

sh 스크립트의 변수를 파일 기본 이름의 마지막 3자로 설정하려고 합니다(기본 이름은 경로가 없음을 의미합니다).그리고접미사 없이). 이 작업을 수행하는 데 성공했지만 순전히 호기심으로 사용할 수 있는 더 짧은 단일 명령이 있는지 궁금합니다. 원래는 한 줄로 된 내용이 있었지만 awk꽤 길었습니다. 현재 다음과 같은 두 줄 스크립트가 있습니다(전체 파일 이름이 에 있다고 가정 $1).

filebase=`basename "$1"`
lastpart=`echo -n ${filebase%.*} | tail -c3`

예를 들어,"/경로/to/somefile.txt"결국"일"안에 $lastpart.

접미사를 단일 명령으로 제거하는 비트와 비트를 어떻게든 결합할 수 있습니까 ? 파이프를 사용하지 않고 basename이를 (또는 사용할 수 있는 다른 것으로) 보낼 수 있는 방법이 있습니까 ? tail접미사를 알 수 없으므로 에 대한 매개변수로 사용할 수 없습니다 basename.

주요 목표는 실제로 가능한 한 짧게 작성하는 것이 아니라 한 눈에 읽을 수 있도록 하는 것입니다. 이 모든 것의 실제 맥락은 다음과 같습니다.슈퍼유저에 대한 이 질문, 여기서 나는 합리적으로 간단한 대답을 찾으려고 노력하고 있습니다.

답변1

var=123456
echo "${var#"${var%???}"}"

###OUTPUT###

456

먼저 마지막 세 문자를 제거한 다음 해당 제거 결과 $var에서 제거합니다 . 그러면 의 마지막 세 문자가 반환됩니다 . 다음은 이러한 작업을 수행하는 방법을 보여주기 위한 보다 구체적인 몇 가지 예입니다.$var$var

touch file.txt
path=${PWD}/file.txt
echo "$path"

/tmp/file.txt

base=${path##*/}
exten=${base#"${base%???}"}
base=${base%."$exten"}
{ 
    echo "$base" 
    echo "$exten" 
    echo "${base}.${exten}" 
    echo "$path"
}

file
txt
file.txt
/tmp/file.txt

너무 많은 명령을 통해 이 모든 것을 퍼뜨릴 필요는 없습니다. 다음과 같이 압축할 수 있습니다.

{
    base=${path##*/} exten= 
    printf %s\\n "${base%.*}" "${exten:=${base#"${base%???}"}}" "$base" "$path"
    echo "$exten"
}

file 
txt 
file.txt 
/tmp/file.txt
txt

$IFSting 쉘 매개변수 와 결합하는 것은 set쉘 변수를 통해 구문 분석하고 드릴링하는 매우 효과적인 수단이 될 수도 있습니다.

(IFS=. ; set -f; set -- ${path##*/}; printf %s "${1#"${1%???}"}")

/그러면 의 마지막 마침표 바로 뒤 의 첫 번째 마침표 바로 앞의 세 문자만 표시됩니다 $path. 마지막 .문자 바로 앞의 처음 세 문자만 검색하려는 경우$path (예를 들어 파일 이름에 둘 이상이 있을 가능성이 있는 경우 .):

(IFS=.; set -f; set -- ${path##*/}; ${3+shift $(($#-2))}; printf %s "${1#"${1%???}"}")

두 경우 모두 다음을 수행할 수 있습니다.

newvar=$(IFS...)

그리고...

(IFS...;printf %s "$2")

...다음 내용을 인쇄합니다..

외부 프로그램을 사용해도 괜찮다면 다음을 수행할 수 있습니다.

printf %s "${path##*/}" | sed 's/.*\(...\)\..*/\1/'

\n파일 이름에 줄바꿈 문자가 포함될 가능성이 있는 경우(네이티브 쉘 솔루션에는 적용되지 않습니다 - 어쨌든 모두 처리합니다):

printf %s "${path##*/}" | sed 'H;$!d;g;s/.*\(...\)\..*/\1/'

답변2

이는 다음의 일반적인 작업입니다 expr.

$ file=/path/to/abcdef.txt
$ expr "/$file" : '.*\([^/.]\{3\}\)\.[^/.]*$'
def

파일 이름이 예상된 형식(단 하나의 점과 점 앞에 최소 3개의 문자 포함)을 갖고 있다는 것을 알고 있는 경우 다음과 같이 단순화할 수 있습니다.

expr "/$file" : '.*\(.\{3\}\)\.'

일치하는 항목이 없으면 종료 상태는 0이 아니지만 일치하는 부분이 0으로 확인되는 숫자인 경우에도 마찬가지입니다. (예: for a000.txt또는 a-00.txt)

와 함께 zsh:

file=/path/to/abcdef.txt
lastpart=${${file:t:r}[-3,-1]}

( :t을 위한꼬리:r(기본 이름 ),나머지(확장자가 제거된 상태)).

답변3

사용할 수 있는 경우 perl:

lastpart=$(
    perl -e 'print substr((split(/\.[^.]*$/,shift))[0], -3, 3)
            ' -- "$(basename -- "$1")"
)

답변4

Perl을 사용할 수 있는 경우 다른 솔루션보다 읽기가 더 쉽다고 생각합니다. 특히 Perl의 정규식 언어가 더 표현력이 뛰어나고 /x더 명확한 정규식을 작성할 수 있는 수정자가 있기 때문입니다.

perl -e 'print $1 if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"

일치하는 항목이 없으면(기본 이름에 확장자가 없거나 확장자 앞의 루트가 너무 짧은 경우) 아무 것도 인쇄하지 않습니다. 요구 사항에 따라 정규식을 조정할 수 있습니다. 이 정규식은 제약 조건을 적용합니다.

  1. 최종 확장자 앞의 3개 문자(마지막 점을 포함한 뒤 부분)와 일치합니다. 이 3개 문자에는 점이 포함될 수 있습니다.
  2. 확장자는 비어 있을 수 있습니다(점 제외).
  3. 일치하는 부분과 확장자는 기본 이름(마지막 슬래시 다음 부분)의 일부여야 합니다.

명령 대체에 이것을 사용하면 후행 줄 바꿈을 너무 많이 제거하는 일반적인 문제가 있으며 이는 Stéphane의 답변에도 영향을 미치는 문제입니다. 두 경우 모두 처리할 수 있지만 여기서는 조금 더 쉽습니다.

lastpart=$(
  perl -e 'print "$1x" if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"
)
lastpart=${lastpart%x}  # allow for possible trailing newline

관련 정보