將一個目標的所有符號連結替換為指向另一個目標(在同一目錄中)的腳本
給定以下人為的範例:
$ mkdir -p a/b && touch a/b/original-target && touch a/b/new-target $ mkdir -p c1/c2 && cd c1/c2 && ln -s ../../a/b/original-target && cd - $ mkdir -p d1/d2/d3 && cd d1/d2/d3 && ln -s ../../../a/b/original-target && cd - $ tree . . ├── a │ └── b │ ├── new-target │ └── original-target ├── c1 │ └── c2 │ └── original-target -> ../../a/b/original-target └── d1 └── d2 └── d3 └── original-target -> ../../../a/b/original-target
如何將所有符號連結替換
original-target
為符號連結到new-target
. 您可以假設原始目標和新目標都位於同一目錄中(如上面人為設計的範例中的情況)。符號連結必須使用相對路徑。
我假設您使用的是 bash,所以我會
globstar
使用**
. 之後,剩下的就是玩一點readlink
,realpath
等:shopt -s globstar for file in c1/** d1/**; do if [[ -h "$file" ]]; then if [[ "$(readlink -f "$file")" == "$(realpath a/b/original-target)" ]]; then ln -sf "$(dirname "$(readlink "$file")")"/new-target "$(dirname "$file")" && rm -f "$file" fi fi done
pax
在這些情況下真的很有用*。*事實上,如果我能找到一個pax
執行POSIX 指定-o listopt=...
選項的選項會更容易,但是,儘管我看起來,我還沒有找到一個這樣做的。我使用 mirabilos 維護的一個 - BSD (**?) - 據我所知,這可能是大多數其他人所做的。無論如何,您可以使用正則表達式文件名 - 和文件列表。例如:pax
mirabipax
(set -e; mkdir -p a/b c1/c2 d1/d2/d3 touch a/b/original-target a/b/new-target cd c1/c2; ln -s ../../a/b/original-target cd ../../d1/d2/d3; ln -s ../../../a/b/original-target)
這就是你的樹。現在我將其列出:
pax -ws'|\(\..*/original-target.*\)*.*|\1|' ././ | pax -cvs'|\(.*\)/original|\1/new|p' \ '././a/b/original-target' '*/original-target?*'
哪個列印…
././d1/d2/d3/original-target >> ././d1/d2/d3/new-target lrwxrwxrwx 1 mikeserv mikeserv 0 Dec 7 18:17 ././d1/d2/d3/new-target => ../../../a/b/new-target ././c1/c2/original-target >> ././c1/c2/new-target lrwxrwxrwx 1 mikeserv mikeserv 0 Dec 7 18:14 ././c1/c2/new-target => ../../a/b/new-target
好的,所以第一個將存檔
pax
寫入. 我交給它的正則表達式 arg 最常用於更改流中的文件名 - 就像我在這裡使用第二個一樣- 但通過一兩個技巧,它也可以用於過濾首先添加到存檔中的文件。ustar``stdout``-s``pax
您會看到,任何在應用 arg 後為空的文件名都
-s
被指定為完全從存檔中刪除。所以我使用一個正則表達式,它將匹配每個文件名,但也會從每個文件中刪除每個與保存在\1
. 這是必要的,因為雖然您可以根據模式輕鬆選擇存檔中的文件,但在-w
riting 存檔時並非如此 - 這取決於 shell glob,因此不會遞歸。儘管如此,所寫的正則表達式仍將
/original-target
跟隨任何字元,因此,在另一端-c
,我在列出檔案時選擇的模式之一是*/original-target?*
- 將其放在那裡。即使這樣也不是完美的——我的意思是,也許你有兩次包含該模式的連結,但是……嗯,它就是這樣——而且非常好。可以通過沿著相同的路線進行進一步的測試來處理它。無論如何,我還-c
對自身進行了匹配././a/b/original-file
- 因此它也從列表輸出中刪除。在列表模式下 -
pax
當它不-r
讀取或-w
儀式時 -詳細pax
列出-格式 - 包括任何軟連結目標- 但僅在應用任何適用的參數之後。無論何時以以下格式應用修飾符,都會列印這些相同的參數:ls -l``link => target``-s``-s``-v``-s|...|...|p``p
“%s>>%s\n”,原始路徑名,新路徑名
所以上面我們得到的列表與
././
不會出現的列表分開。現在這讓事情變得非常容易,我希望你會同意。我將把它留給讀者來決定如何應用這種數據ln
——它應該相當簡單——但你甚至不需要這樣做。事實上,
pax
規範允許您使用 cli 選項在r
提前或正式時間更改存檔,以更改使用者、組、連結數據、文件名、文件源等。這些是非常強大的文件解析工具 - 但是w
我最接近pax
實現它們的是 GNUtar
,而且只是部分實現。我還沒有找到任何影響標頭檔類型的方法——儘管你應該能夠在某種程度上做到這一點——使用 cli 選項,即使在存檔本身上使用正則表達式並不是非常困難。這些東西有一個標題欄位:… 2 - 表示符號連結。符號連結的內容應儲存在連結名稱欄位中。
我真正想做的另一件事
pax
是-o listopt=...
。這是規範範例部分的片段:使用選項:
-o listopt="%M %(atime)T %(size)D %(name)s"
…覆蓋標準輸出中的預設輸出描述,而是寫入:
-rw-rw--- Jan 12 15:53 2003 1492 /usr/foo/bar
使用選項:
-o listopt='%L\t%(size)D\n%.7' \ -o listopt='(name)s\n%(atime)T\n%T'
…覆蓋標準輸出中的預設輸出描述,而是寫入:
/usr/foo/bar -> /tmp 1492 /usr/fo Jan 12 15:53 1991 Jan 31 15:53 2003
因此,如果您偶然發現
pax
這樣做的…好吧,您知道誰在尋找。