2013-02-22 21 views
11

我有一個腳本只能將數據寫入stdout。我需要運行它爲多個文件,併爲每個輸入文件生成不同的輸出文件,我想知道如何使用find -exec。所以我基本上嘗試的這幾款變種(我更換了劇本由cat只是測試性的目的):使用find -exec重定向stdout並且不創建新的shell

找到*型的F -exec貓 「{}」> 「{} .stdout」 \;

但由於所有的數據都被寫入一個字面上名爲{}.stdout的文件,所以無法工作。

最後,我可以把它一起工作:

發現*型的F -exec SH -c 「貓{}> {} .stdout」 \;

不過,雖然這種最新形式與cat效果很好,我的腳本需要通過幾個初始化腳本加載環境變量,因此,我結束了:

發現*型的F -exec SH -c「 initscript1; initscript2; ...; myscript {}> {} .stdout「\;

這似乎是一種浪費,因爲我已經在我的當前shell中初始化了一切。

有沒有更好的方法來做到這一點find?其他單線也歡迎。

+2

如果它們在原始shell中初始化,但未在子shell中設置,則它們不是環境變量。在你的腳本的頂部寫上'set -a'。 – 2013-02-22 18:23:25

+0

你給出的最後一個例子是正確的還是命令:'find。 -type f -exec sh -c「。initscript1;。initscript2; ...; myscript {}> {} .stdout」\; '(而不是簡單地調用'initscript1',你實際上是否在調用'.initscript1',即你正在使用點命令來源文件)。 – 2013-02-22 18:30:11

回答

5

簡單的解決辦法是把一個包裝器腳本:

#!/bin/sh 

myscript "$1" > "$1.stdout" 

說它myscript2與查找調用它:

find . -type f -exec myscript2 {} \; 

注意,雖然大多數實現找到讓你做你所做的事情,從技術上來說,如果在-exec的參數列表中多次使用{},那麼find的行爲是未指定的。

+2

但是在'find'手冊中,在'-exec'的某個地方有這樣的說法:_字符串'{}'被替換爲當前文件名,無論它出現在命令的參數中的任何地方,就像在某些版本的find._ [link](http://unixhelp.ed.ac.uk/CGI/man-cgi?find)中一樣。不過,感謝您的解決方法。 – jserras 2013-02-22 22:14:38

+3

您的'find'的特定實現手冊聲明它的工作原理,但標準內容如下:'如果包含兩個字符的多個參數「{}」存在,則行爲未指定。「這不是什麼大問題,但它可能會燒你(在這一點上,它突然變得非常重要!) – 2013-02-22 22:32:02

+3

一個更重要的缺點是,諸如'-exec sh -c「myscript {}> {} .stdout」\;'可能導致面對惡意文件名的任意代碼執行。執行'-exec sh -c'myscript「$ 1」>「$ 1.stdout」'sh {} \;'會更安全。 – jilles 2013-02-22 23:50:38

2

你可以用eval來做到這一點。它可能很難看,但爲此必須製作一個shell腳本。另外,這一切都在一條線上。 例如

find -type f -exec bash -c "eval md5sum {} > {}.sum " \; 
+0

'bash -c'是這裏的牛肉,'eval'實際上並沒有做任何有用的事情。但是你並沒有躲避外殼。 – tripleee 2017-03-21 16:00:22

+0

如果你拿出'eval',我認爲這應該是實際上被接受的答案,儘管OP會放棄避開殼。 (將腳本放在單獨的文件中時,無論如何都要在運行該腳本時創建一個shell,OP所要求的並不是真的可能。) – tripleee 2017-03-21 16:02:30

+0

「eval」在這裏非常危險。如果你有一個包含'$(rm -rf $ HOME)'的文件名,這將會是非常糟糕的消息。 – 2017-04-05 17:27:48

2

如果出口你的環境變量,他們就已經存在於子shell(如果使用bash -c代替sh -c,和您的父母殼本身的bash,那麼你也可以在父shell中導出函數,並將它們用於子項中;請參閱export -f)。

此外,通過使用-exec ... {} +,你可以限制彈所需的數量儘可能少的數量,通過在命令行上所有參數:

set -a # turn on automatic export of all variables 
source initscript1 
source initscript2 

# pass as many filenames as possible to each sh -c, iterating over them directly 
find * -name '*.stdout' -prune -o -type f \ 
    -exec sh -c 'for arg; do myscript "$arg" > "${arg}.stdout"' _ {} + 

或者,你可以在你的當前執行的執行直接殼:

while IFS= read -r -d '' filename; do 
    myscript "$filename" >"${filename}.out" 
done < <(find * -name '*.stdout' -prune -o -type f -print0) 

參見UsingFind安全地討論和通過find正確地履行批量操作;和BashFAQ #24討論使用進程替換(<(...)語法)以確保操作在父shell中執行。

+0

使用'_'作爲$ 0來調用sh有點混淆! – 2017-04-05 17:50:06

+0

@WilliamPursell,這是一個常見的成語 - 可以找到鏈接,如果你喜歡。 ('_'在其他一些語言中也是一個常規的未使用/佔位符值,比如Python,但我的理解是它首先在shell中很常見)。 – 2017-04-05 18:01:04

+0

我已經看到它在go和perl中使用,但從未在此設置中使用過。我傾向於忽略它,並將$ 0設置爲{},這可能是一種非常糟糕的做法! – 2017-04-05 18:40:02

相關問題