
隨著時間的推移,我一次又一次遇到相同的模式:我有某種目錄結構:
example/
├── a
│ └── c
│ ├── d.txt (120k)
│ └── e.txt (60k)
└── b
└── f.txt (280k)
我想將文件“複製”到另一個目錄,例如,example_grepped
對每個文件應用一個命令,就像代替cp
- 比如說,grep ERROR
這樣說,我最終得到一個具有相同結構的文件夾,但文件通過grep
。
example_grepped/
├── a
│ └── c
│ ├── d.txt (1k)
│ └── e.txt (0b)
└── b
└── f.txt (12k)
轉換媒體檔案(FLAC 到 MP3、PNG 到 JPG)的模式相同,這次在建置過程中轉換不同的架構格式。
有我可以使用的通用命令嗎?類似foobar example example_grepped --command 'grep ERROR'
或foobar flacs mp3s --command 'ffmpeg -i {} {}.mp3'
?
也許是一面不起眼的xargs
旗幟? (find
透過管道xargs
將幾乎足夠了,但大多數(如果不是全部)命令都期望目錄結構已經存在。
答案1
我可以找到的最接近的答案,無需單獨重新建立目錄結構,就是使用安裝:
cd example
find . -type f -exec sh -c 'grep ERROR {} | install -D /dev/stdin /tmp/example_grepped/{}' \;
不幸的是,只有當您的命令可以將其結果拋出到 STDOUT 時,上述內容才有效。
答案2
解決此問題的另一種方法是使用無論如何都會進行遞歸複製的程序。我查了一下rsync
,但快速瀏覽後找不到回調選項。但是 gnutar
有一個選項--to-command
,您可以提供一個要運行的命令,將文件的輸入獲取到stdin
.但是如何建立文件呢?好吧,呼叫的命令會尋找 中的目前檔名$TAR_FILENAME
。
把它們放在一起,基本的呼叫是
tar cf - example | tar xf - --to-command="./script example_grepped 'grep-pattern'"
其中腳本可能類似於
#!/bin/bash
mkdir -p $(dirname "$1/$TAR_FILENAME")
grep '$2' >"$1/$TAR_FILENAME"
exit 0
解決此問題的另一種方法是將 tar 管道包裝在腳本中,該腳本可使命令在命令列上執行。然而,該構造的轉義mkdir ...dirname
將有點具有挑戰性。
答案3
#!/bin/bash
filter() {
local target_root="${@: -1}"
target_path=$(sed -E "s/[^/]*/$target_root/" <<< "$1")
target_dir=$(dirname "$target_path")
mkdir -p "$target_dir"
if [[ -f $1 ]]; then
# do your grep thing here
grep burger "$1" > "$target_path"
fi
}
export -f filter
source_root="example"
target_root="example_grepped"
find "$source_root/" -print0 | xargs -0 -I content bash -c "filter 'content' '$target_root'"
此腳本也適用於包含空格的目錄和檔案名稱。
在來源目錄(“example”)所在的位置執行此腳本。
答案4
使用 GNU Parallel 你可以做這樣的事情:
cd src
find . -type f | parallel 'mkdir -p ../dst/{//}; dostuff --input {} --output ../dst/{}'