/ path / to / file을 / p / t / file로 축약하는 방법


9

나는 awk각 부모 / 중급 수준의 첫 문자를 사용하여 유닉스 경로 문자열을 단축시키는 완전한 1 줄짜리 (예 :)를 찾고 있습니다. 예제로 쉽게 보여주기 :

  • /path/to/file/p/t/file
  • /tmp/tmp
  • /foo/bar/.config/wizard_magic/f/b/./wizard_magic
  • /foo/bar/.config/wizard_magic/f/b/.c/wizard_magic
    아래 @ MichaelKjörling 및 @ChrisH의 장점을 고려하여이 예는 첫 번째 문자가 점일 때 처음 두 문자를 표시하는 방법을 보여줍니다.

제안 (귀하의 유스 케이스를 모른다) : 대신에 약어 /f/b/.c/wizard_magic. 점은 종종 특정 디렉토리에서 매우 일반적이므로 찾고있는 곳의 아주 작은 단서가됩니다.
Chris H

@ChrisH가 말한 것 외에도 .일반적으로 "현재 디렉토리"를 의미합니다. 그래서 /f/b/./wizard_magic와 동일한 /f/b/wizard_magic경로 요소 때문에 ./빈 경로 요소로 압축합니다.
CVn

왜 그런가요? 대화 형 쉘에서 영리한 자동 완성 기능을 사용할 수 없음 (아마도 쉘을 적절한 것으로 변경)
Basile Starynkevitch

답변:


7

이 테스트 파일의 경우 :

$ cat path
/path/to/file
/tmp
/foo/bar/.config/wizard_magic

이 awk 코드로 약어를 생성 할 수 있습니다.

$ awk -F/ '{for (i=1;i<NF;i++) $i=substr($i,1,1)} 1' OFS=/ path
/p/t/file
/tmp
/f/b/./wizard_magic

Edit1 : 도트 이름에 두 문자 사용

이 버전은 디렉토리 이름을 한 문자 .로 줄여서 시작하는 이름은 두 문자로 약자를 사용합니다.

$ awk -F/ '{for (i=1;i<NF;i++) $i=substr($i,1,1+($i~/^[.]/))} 1' OFS=/ path
/p/t/file
/tmp
/f/b/.c/wizard_magic

작동 원리

  • -F/

    이것은 awk에게 슬래시를 입력시 필드 구분자로 사용하도록 지시합니다.

  • for (i=1;i<NF;i++) $i=substr($i,1,1)

    마지막 필드를 제외한 각 필드를 반복하여 첫 번째 문자로 바꿉니다.

    EDIT1 : 수정 된 버전에서 필드가로 시작할 때 부분 문자열 2의 길이를 만듭니다 ..

  • 1

    이것은 awk에게 수정 된 라인을 인쇄하도록 지시합니다.

  • OFS=/

    이것은 awk에게 슬래시를 출력에서 ​​필드 분리 자로 사용하도록 지시합니다.


탁월한 답변, 구분 기호 를 사용하도록 약간 수정 : awk -F/ '{for (i=1;i<NF;i++) $i=substr($i,1,1+($i~/^[.]/))(i==1||length($i)<2?"":"‥")} 1' OFS=/ <<<$PWD제공 : /foo/bar/.config/wizard_magic/f‥/b‥/.c‥/wizard_magic
ideasman42

12

sed에서 꽤 쉽습니다 (파일 이름에 줄 바꿈이 없다고 가정) :

sed 's!\([^/]\)[^/]*/!\1/!g'

역 참조가 없기 때문에 awk에서 덜 쉽습니다 (Gawk 제외하지만 서투른 구문 사용).

awk -v FS=/ -v OFS=/ '{for (i=1; i<NF; i++) $i=substr($i,1,1)} 1'

zsh에서 (경로가있는 $full_path) :

echo "${(j:/:)${(@r:1:)${(@s:/:)${full_path:h}}}}/${full_path:t}"

2
IIRC "역 참조"는 대체 문자열이 아닌 패턴에서 발생하는 그룹을 캡처하기위한 참조입니다.
Rhymoid

@Rhymoid \1대체 문자열은 않는 패턴에서 캡처 그룹에 대한 참조를 의미한다. 역 참조는 어디에서 사용하든 역 참조입니다.
Gilles 'SO- 악의를 그만두십시오

8

당신은 다음과 같이 할 수 있습니다 :

cd /usr///.//share/../share//man/man1 || exit
IFS=/; set -f
printf %.1s/  ${PWD%/*}
printf %s\\n "${PWD##*/}"

/u/s/m/man1

그리고 여기에 sed:

printf %s "$file" |
tr /\\n \\n/      | sed -et$ \
    -e '\|^\.\.$|{x;s|\(.*\)\n.*$|\1|;x;}'  \
    -e 's|^\.\{0,2\}$||;\|.|H;$!d;x'        \
-e$ -e '\|\(\.\{0,2\}.\)\(.*\)\(\n\)|!b'    \
    -e 's||\1\3\2\3|;P;s|\n||;D' |
tr /\\n \\n/

그것은 함수가 아래에서하는 것과 똑같은 일을하는 것과 아주 가깝습니다. $PWD함수가하는 것처럼 슬래시로 머리 글자를 생략하거나 머리에 삽입하여 슬래시를 선행하지 않습니다 (실제로 슬래시를 인쇄하지 않음). 그러나 나중에 처리 할 수 ​​있습니다. 널 (null) 경로 구성 요소 및 단일 도트를 처리하고 ..케이스를 제거합니다.

위와 같은 man경로가 주어지면 다음과 cd같이 인쇄됩니다.

u/s/m/man1

또한 시작점으로 시작하고 하나 또는 두 개의 점이 아닌 각 경로 구성 요소에 대해 하나 또는 두 개의 추가 선행 점을 인쇄합니다.

로 시작하는 경로 구성 요소에 대해 두 개 이상의 문자를 수행하는 방법에 대해 질문했습니다 .. 그것을하기 위해 나는 어쨌든 각각의 구성 요소가 개별적인주의를 필요로한다고 생각했고, 궁금해서 변경 디렉토리없이 정식 경로를 만드는 데 손을 댔다. 시행 착오 후에 나는 그것을 올바르게하는 유일한 방법은 그것을 앞뒤로 두 번하는 것이라고 결정했습니다.

pathbytes(){
    local IFS=/   o="$-" p
    set -f${ZSH_VERSION+LFy}
    set -- ${1:-$PWD}
    for p   in      /${1:+$PWD} $*
    do      case    $p in   (.|"")  ;;
            (..)    ${1+shift}      ;;
            (/)     set --          ;;
            (*)     set -- $p $*;   esac
    done
    for p   in      //$* ""
    do      case   ${p:-/$3}        in
            ([!./]*)                ;;
            (..*)   set "..$@"      ;;
            (.*)    set ".$@"       ;;
            (//*) ! set "" $1 $1    ;;
            (~)   ! p=\~            ;;
            (~/*)   p="~/$2";set $HOME
                  ! while "${2+shift}" 2>&3
                    do   p="~/${p#??*/}"
                    done 3>/dev/null;;
            esac&&  set ""  "${p%"${p#$1?}"}/$2" "$p/$3"
    done;   printf %s\\n "${p:-$2}"
    set +f  "-${o:--}"
}

따라서 디렉토리를 변경하지 않거나 경로 구성 요소의 존재를 확인하려고 시도하지만 반복되는 /구분 기호를 누르고 /./단일 도트 구성 요소를 완전히 삭제 하고 /../이중 도트 구성 요소를 적절하게 처리합니다.

공백이 아닌 문자$IFS 로 설정된 경우 , 두 개 이상의 문자 시퀀스는 하나 이상의 널 (null) 필드가됩니다. 따라서 여러 개의 연속 슬래시는 널값 인수로 해결됩니다. 주인공도 마찬가지입니다 . 분할 할 때 결과 가 null 인 경우 슬래시로 시작하고 null이 아닌 경우 i 삽입 합니다. 즉, 첫 번째 인수가 슬래시로 시작하지 않으면 앞에 추가됩니다. 이것이 경로 유효성 검사 와 관련이 있습니다.$IFS$IFSset -- $1$1${1:+$PWD}$PWD$PWD

그렇지 않으면 첫 번째 for루프는 다음과 같이 경로 구성 요소의 순서를 재귀 적으로 반전시킵니다.

      1 2 3
1     2 3
2 1   3
3 2 1

... 그렇게하는 동안 단일 점 또는 null 구성 요소를 무시하고 그렇게합니다 .....

      1 .. 3
1     .. 3
      3
3

... 두 번째 패스는이 효과를 반대로하고, 그렇게하는 동안 각 구성 요소를 2-dots + char 또는 1-dot + char 또는 char로 압착합니다 .

존재 여부에 관계없이 표준 경로로 작동해야합니다.

두 번째 루프에 약간 더하거나 뺍니다. 이제는 set빈도가 줄어들고 (각 [!./]*구성 요소 에 대해 한 번만 ) , case대부분의 경우 회로 패턴 평가 (앞서 언급 한 패턴 덕분에)를 단락 시키고에 대한 테일 콜 매치 평가를 포함 ~합니다. 최종 정식 경로의 전체 또는 선행 부분 (전체 구성 요소로 나눈 값) 이 일치 할 수 있으면 일치 ~하는 비트가 제거되고 리터럴 ~이 대체됩니다. 이렇게하려면 약어와 함께 경로의 전체 사본을 유지해야했습니다 (약어 경로를 일치시키는 ~것이별로 도움이되지 않기 때문에) . 그래서 이것은 유지됩니다 $3. 마지막while루프 분기는 ~의 하위 집합과 일치 하는 경우에만 실행됩니다 $3.

set -x추적 기능을 사용 하여 실행하면 작동하는 것을 볼 수 있습니다.

$ (set -x;pathbytes ..abc/def/123///././//.././../.xzy/mno)
+ pathbytes ..abc/def/123///././//.././../.xzy/mno
+ local IFS=/ o=xsmi p
+ set -f
+ set -- ..abc def 123   . .   .. . .. .xzy mno
+ set --
+ set -- home
+ set -- mikeserv home
+ set -- ..abc mikeserv home
+ set -- def ..abc mikeserv home
+ set -- 123 def ..abc mikeserv home
+ shift
+ shift
+ set -- .xzy ..abc mikeserv home
+ set -- mno .xzy ..abc mikeserv home
+ set  mno mno
+ set . mno mno
+ set  .x/mno .xzy/mno
+ set .. .x/mno .xzy/mno
+ set  ..a/.x/mno ..abc/.xzy/mno
+ set  m/..a/.x/mno mikeserv/..abc/.xzy/mno
+ set  h/m/..a/.x/mno home/mikeserv/..abc/.xzy/mno
+ p=~/h/m/..a/.x/mno
+ set  home mikeserv
+ shift
+ p=~/m/..a/.x/mno
+ shift
+ p=~/..a/.x/mno
+
+ printf %s\n ~/..a/.x/mno
~/..a/.x/mno
+ set +f -xsmi

4
시원하지만 내 눈이 아프다.
glenn jackman

1
@don_crissti-그렇습니다!
mikeserv

2

Oh My Zsh 의 "fishy"Zsh 테마 에는 유니 코드를 지원하는 Perl 스 니펫이 포함되어 있습니다.

perl -pe '
   BEGIN {
      binmode STDIN,  ":encoding(UTF-8)";
      binmode STDOUT, ":encoding(UTF-8)";
   }; s|^$HOME|~|g; s|/([^/.])[^/]*(?=/)|/$1|g; s|/\.([^/])[^/]*(?=/)|/.$1|g;
'

1

짧은 이름을 사용하거나 명령 줄에 사용 하시겠습니까?
커맨드 라인의 경우 다음 제안 사항
이 있습니다. 셸에서 파일 완성이 도움이되지 않습니까?
때로는 운이 좋으며 특별한 것을 할 필요가 없습니다.

# /path/to/file -> /p/t/file
ls -l /*/*/file 

# /tmp -> /tmp
cd /tmp

# /foo/bar/.config/wizard_magic -> /f/b/./wizard_magic
ls -l /*/*/*/wizard_magic -> /f/b/./wizard_magic

관심있는 디렉토리가 있으면 별명을 사용할 수 있습니다.

alias cdto="cd /path/to"
alias cdtmp="cd /tmp"
alias cdcfg="cd /foo/bar/.config"
alias cddeep="cd /home/john/workdir/project1/version3/maven/x/y/z/and/more"

또는 선호하는 디렉토리에 대한 변수를 설정할 수 있습니다

export p="/path/to"
export f="/foo/bar/.config"
ls -l $p/file
ls -l $f/wizard_magic

이 옵션은 .bashrc (또는 .profile)에 정의 된 함수 로이 문제를 해결하는 것보다 더 의미가 있다고 생각합니다.

function x { 
   xxpath=""
   while [ $# -ne 0 ]; do
     xxpath+="${1}*/"
     shift
   done
   cd $(echo "${xxpath}")
}

문자 사이에 공백이있는이 함수 x를 호출하십시오.

 # cd /path/to
 x /p t

 # cd /tmp 
 x /t

 # cd /foo/bar/.config
 x /f b 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.