기본 파일 이름의 마지막 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%???}"}")

/이렇게 하면 의 마지막 마침표 뒤 첫 번째 마침표 바로 앞의 3개 문자만 제공됩니다 $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

관련 정보