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

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

awk각 상위/중간 레벨의 첫 번째 문자를 사용하여 Unix 경로의 문자열을 단축하지만 전체 기본 이름을 사용하는 우아한 한 줄짜리 코드(예: )를 찾고 있습니다 . 예를 들어 설명하는 것이 더 쉽습니다.

  • /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의 좋은 점을 고려하여 이 예에서는 첫 번째 문자가 점일 때 처음 두 문자를 표시하는 방법을 보여줍니다.

답변1

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

다음을 수행할 수 있습니다.

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슬래시가 아닌 선행 문자로 헤더에 삽입됩니다.(실제로 선행 슬래시는 인쇄되지 않습니다.)하지만 이 문제는 나중에 다룰 수 있습니다. 빈 경로 구성 요소와 단일 지점을 처리하고 ..사례를 지웁니다.

man위와 동일한 경로가 주어지면 cd다음이 인쇄됩니다.

u/s/m/man1

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

로 시작하는 경로 구성 요소에 대해 여러 문자를 사용할 것인지 묻습니다 .. 그러기 위해서는 각 구성 요소에 개별적인 주의가 필요하다고 판단했고, 궁금했기 때문에 디렉터리를 변경하지 않고 표준 경로를 알아내려고 노력했습니다. 몇 번의 시행착오 끝에 마침내 이 작업을 올바르게 수행하는 유일한 방법은 이 작업을 앞뒤로 두 번 수행하는 것이라고 결정했습니다.

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일부로 설정 하면공백이 아닌문자가 있는 경우 두 개 이상의 문자 시퀀스 $IFS로 인해 하나 이상의 빈 필드가 생성됩니다. 따라서 여러 개의 연속 슬래시가 null 매개변수를 계산할 수 있습니다. 주인공도 마찬가지다 $IFS. 따라서 set -- $1분할할 때 결과가 $1비어 있으면 슬래시로 시작하고, ${1:+$PWD}비어 있지 않으면 를 삽입합니다 $PWD. 즉, 첫 번째 인수가 슬래시로 시작하지 않으면 $PWDFront에 추가됩니다. 이 길이 가장 가깝습니다확인하다.

그렇지 않으면 첫 번째 for루프는 경로 구성 요소의 순서를 재귀적으로 반대로 바꿉니다. 예를 들면 다음과 같습니다.

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

...이 작업을 수행할 때 단일 점이나 빈 구성 요소는 무시됩니다 ...

      1 .. 3
1     .. 3
      3
3

...두 번째 패스는 이 효과를 반전시키고 그렇게 함으로써 각 구성요소를 둘 중 하나로 압착합니다.2포인트 + 문자, 또는1포인트 + 문자, 또는성격.

따라서 존재 여부에 관계없이 표준 경로를 따라야 합니다.

두 번째 루프에서 비트를 더하거나 뺍니다. 이제 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

답변3

이 테스트 파일의 경우:

$ 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에게 출력에서 ​​슬래시를 필드 구분 기호로 사용하도록 지시합니다.

답변4

짧은 이름을 사용하시겠습니까, 아니면 명령줄에서 사용하시겠습니까?
명령줄에 대해 다음 제안 사항이 있습니다.
셸에서 파일 완성이 도움이 되지 않습니까?
운이 좋아서 특별한 일을 할 필요가 없는 경우도 있습니다.

# /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 

관련 정보