/ path / to / fileを/ p / t / fileに短縮する方法


9

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による良い点を考慮して、この例では、最初の文字がドットの場合に最初の2文字を表示する方法を示しています。

提案(私はあなたのユースケースを知りません):代わりにに省略してください/f/b/.c/wizard_magic。ドットは、特定のディレクトリでよく見られるため、どこを見ればよいかが非常にわかりにくい場合があります。
Chris H

@ChrisHが言ったことに加えて、.通常は単に「現在のディレクトリ」を意味します。これ/f/b/./wizard_magicは、/f/b/wizard_magicpath要素が./空のpath要素に圧縮されるためと同じです。
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:ドット名に2文字を使用する

このバージョンでは、ディレクトリ名が1文字に短縮されます。ただし、先頭.が2文字に短縮される場合は除きます。

$ 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 2015

@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、関数のように先頭の非スラッシュの先頭にを挿入したりはしません(実際、先頭のスラッシュは出力しません)が、後で処理することはできます。ヌルパスコンポーネントと単一ドットを処理し、..ケースを除外します。

上記と同じmanパスを指定すると、次のcdように出力されます。

u/s/m/man1

また、1つまたは2つのドットではなく、そのようなパスで始まる各パスコンポーネントに対して1つまたは2つの余分な先行ドットを印刷します。

で始まるパスコンポーネントに対して複数の文字を実行することについて質問しました.。それを行うには、とにかく各コンポーネントに個別の注意が必要だと考えました。私は好奇心が強いので、ディレクトリを変更せずに正規のパスを作成することを試みました。いくつかの試行錯誤の後、私は結局それを正しく行う唯一の方法はそれを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がnullの場合はスラッシュで始まり、それ以外の${1:+$PWD}場合はnullでない場合はを挿入し$PWDます。つまり、最初の引数がスラッシュで始まらない場合は、$PWD先頭に追加されます。これは、パス検証の場合と同じです。

それ以外の場合、最初のforループはパスコンポーネントの順序を次のように再帰的に反転します。

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

...その間、単一ドットまたはnullコンポーネントは無視されますが、そのため..に...

      1 .. 3
1     .. 3
      3
3

... 2番目のパスはこの効果を元に戻し、その間、各コンポーネントを2ドット+チャー、または1ドット+チャー、またはチャーのいずれかに圧縮します。

したがって、存在するかどうかに関係なく、正規のパスに到達するはずです。

2番目のループに少し追加/減算しました。今ではsetそれほど頻繁ではなく([!./]*コンポーネントごとに1回のみ)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
かっこいいけど目が痛い。
グレン・ジャックマン、2015

1
@don_crissti-はい!
mikeserv、2015

2

Oh My Zsh の「魚のような」Zshテーマには、Unicodeをサポートすることを行うための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.