2010-02-25 57 views
4

我試圖逃避用戶提供的搜索字符串,它可以包含任意字符並將其提供給sed,但無法弄清楚如何使sed安全使用。在sed中,我們做s/search/replace/,我想在搜索字符串中搜索完整的字符而不用sed解釋它們(例如,'my/path'中的'/'不會關閉sed表達式)。你如何逃避用戶提供的搜索詞,你不想評估sed?

我讀this related question有關如何逃生替換任期。我原以爲你會去search做同樣的事情,但顯然不是因爲sed抱怨。

下面是一個示例程序,它創建一個名爲「my_searches」的文件。然後它讀取該文件的每一行並執行搜索並使用sed進行替換。

#!/bin/bash 

# The contents of this heredoc will be the lines of our file. 
read -d '' SAMPLES << 'EOF' 
/usr/include 
[email protected]$$W0RD$? 
"I didn't", said Jane O'Brien. 
`ls -l` 
[email protected]#$%^&*()_+-=:'}{[]/.,`"\| 
EOF 
echo "$SAMPLES" > my_searches 

# Now for each line in the file, do some search and replace 
while read line 
do 
     echo "------===[ BEGIN $line ]===------" 

     # Escape every character in $line (e.g., ab/c becomes \a\b\/\c). I got 
     # this solution from the accepted answer in the linked SO question. 
     ES=$(echo "$line" | awk '{gsub(".", "\\\\&");print}') 

     # Search for the line we read from the file and replace it with 
     # the text "replaced" 
     sed 's/'"$ES"'/replaced/' < my_searches  # Does not work 

     # Search for the text "Jane" and replace it with the line we read. 
     sed 's/Jane/'"$ES"'/' < my_searches   # Works 

     # Search for the line we read and replace it with itself. 
     sed 's/'"$ES"'/'"$ES"'/' < my_searches  # Does not work 

     echo "------===[ END ]===------" 
     echo 
done < my_searches 

當你運行程序,你會得到sed: xregcomp: Invalid content of \{\}該文件的最後一行時,它作爲「搜索」一詞,而不是「取代」一詞。我在上面標記了# Does not work這個錯誤的行。

------===[ BEGIN [email protected]#$%^&*()_+-=:'}{[]/.,`"| ]===------ 
sed: xregcomp: Invalid content of \{\} 
------===[ END ]===------ 

如果不逃的字符$line(即sed 's/'"$line"'/replaced/' < my_searches),你得到這個錯誤,而不是因爲SED試圖解釋各種人物:

------===[ BEGIN [email protected]#$%^&*()_+-=:'}{[]/.,`"| ]===------ 
sed: bad format in substitution expression 
sed: No previous regexp. 
------===[ END ]===------ 

那麼,如何逃脫搜索term for sed,以便用戶可以提供任意文本來搜索?或者更確切地說,我可以用我的代碼中的ES=行代替什麼,以便sed命令適用於文件中的任意文本?

我使用sed,因爲我僅限於busybox中包含的實用程序子集。雖然我可以使用其他方法(如C程序),但確實知道是否有解決此問題的方法是很好的。

回答

0

這個:echo "$line" | awk '{gsub(".", "\\\\&");print}'轉義$line中的每個字符,這是錯誤的!在那之後做一個echo $ES,$ ES看起來是\/\u\s\r\/\i\n\c\l\u\d\e。然後,當你傳遞給下一個SED,(下)

sed 's/'"$ES"'/replaced/' my_searches 

,它不會工作,因爲沒有行有格局\/\u\s\r\/\i\n\c\l\u\d\e。正確的方法是這樣的:

$ sed 's|\([@$#^&*!~+-={}/]\)|\\\1|g' file 
\/usr\/include 
P\@\$\$W0RD\$? 
"I didn't", said Jane O'Brien. 
\`ls -l\` 
\~\!\@\#\$%\^\&\*()_\+-\=:'\}\{[]\/.,\`"\| 

你把你想裏面[]躲過了所有的字符,以及選擇合適的分隔符的sed,是不是在你的字符類,比如我選擇的是「|」。然後使用「g」(全局)標誌。

告訴我們你實際上想要做什麼,即你正試圖解決的實際問題。

+0

這是我試圖解決的實際問題。我正在從包含用戶輸入的字符串的文件中讀取一行,並用另一個字符串(也包含用戶輸入的數據)替換它。我使用bash和sed,因爲我有一套有限的實用程序(busybox)。我試圖讓用戶輸入任何可能的字符,並仍然在sed表達式中工作。 – indiv 2010-02-25 16:54:13

0

由於幽靈狗提到,awk '{gsub(".", "\\\\&");print}'是不正確的,因爲它逃脫了非特殊字符。你真正想做的事情可能是這樣的:

awk 'gsub(/[^[:alpha:]]/, "\\\\&")' 

這將逃避非alpha字符。出於某種原因,我還沒有確定,我仍然無法取代"I didn't", said Jane O'Brien.即使我上面的代碼正確,它避開了對

\"I\ didn\'t\"\,\ said\ Jane\ O\'Brien\.

這很奇怪,因爲這工作完全正常

$ echo "\"I didn't\", said Jane O'Brien." | sed s/\"I\ didn\'t\"\,\ said\ Jane\ O\'Brien\./replaced/ 
replaced` 
1

這是一個比較有名的問題—給出了一個字符串,產生一個只匹配那個字符串的模式。在某些語言中比其他語言更容易,sed是令人討厭的語言之一。我的建議是避免sed並用其他語言編寫自定義程序。

  • 您可以使用標準庫函數strstr編寫自定義C程序。如果速度不夠快,您可以使用Google —中的任何Boyer-Moore字符串匹配器,他們將使搜索速度非常快(次線性時間)。通過應用quotearg[1]只有一次

    local function quote(s) return (s:gsub('%W', '%%%1')) end 
    local function replace(first, second, s) 
        return (s:gsub(quote(first), second)) 
    end 
    for l in io.lines() do io.write(replace(arg[1], arg[2], l), '\n') end 
    

    如果不夠快,加快速度,並內嵌frunciton replace

  • 你可以在Lua寫這篇文章很輕鬆了。

0

這似乎爲FreeBSD工作的sed:FreeBSD的SED的

# using FreeBSD & Mac OS X sed 
ES="$(printf "%q" "${line}")" 
ES="${ES//+/\\+}" 
sed -E s$'\777'"${ES}"$'\777'replaced$'\777' < my_searches 
sed -E s$'\777'Jane$'\777'"${line}"$'\777' < my_searches 
sed -E s$'\777'"${ES}"$'\777'"${line}"$'\777' < my_searches 
+0

什麼是-E?我沒有在我的sed中,也沒有在gnu文檔中看到它:http://www.gnu.org/software/sed/manual/sed.html ..小寫字母-e不能與相同的「xregcomp:\ {\}的無效內容」。看起來\ \和\}對sed有點意義,但我沒有研究過。 – indiv 2010-02-25 16:56:35

0

-E選項用於開啓擴展正則表達式。

GNU sed也可以分別通過-r或--regexp-extended選項使用。

對於基本和擴展正則表達式看到之間的差異,例如:

http://www.gnu.org/software/sed/manual/sed.html#Extended-regexps

也許你可以使用FreeBSD的兼容minised而不是GNU sed的?

# example using FreeBSD-compatible minised, 
# http://www.exactcode.de/site/open_source/minised/ 

# escape some punctuation characters with printf 
help printf 
printf "%s\n" '!"#$%&'"'"'()*+,-./:;<=>[email protected][\]^_`{|}~' 
printf "%q\n" '!"#$%&'"'"'()*+,-./:;<=>[email protected][\]^_`{|}~' 

# example line 
line='!"#$%&'"'"'()*+,-./:;<=>[email protected][\]^_`{|}~ ... and Jane ...' 

# escapes in regular expression 
ES="$(printf "%q" "${line}")"  # escape some punctuation characters 
ES="${ES//./\\.}"     # . -> \. 
ES="${ES//\\\\(/(}"     # \(-> (
ES="${ES//\\\\)/)}"     # \) ->) 

# escapes in replacement string 
lineEscaped="${line//&/\&}"   # & -> \& 

minised s$'\777'"${ES}"$'\777'REPLACED$'\777' <<< "${line}" 
minised s$'\777'Jane$'\777'"${lineEscaped}"$'\777' <<< "${line}" 
minised s$'\777'"${ES}"$'\777'"${lineEscaped}"$'\777' <<< "${line}" 
0

爲了避免潛在的反斜線混亂,我們可以(或者說應該)使用反斜槓變量,像這樣:

backSlash='\\' 
ES="${ES//${backSlash}(/(}" # \(-> (    
ES="${ES//${backSlash})/)}" # \) ->) 

(通過使用變量,這樣的方式似乎是一個好方法應對參數擴展問題...)

0

...或完成反斜槓混亂......

backSlash='\\' 
lineEscaped="${line//${backSlash}/${backSlash}}" # double backslashes 
lineEscaped="${lineEscaped//&/\&}"     # & -> \& 
0

如果你有bash,而你只是在做一個模式替換,只需在bash中本地執行。 Bash中的${parameter/pattern/string}擴展對你來說非常有用,因爲你只需要使用一個變量來代替「模式」和替換「字符串」,並且變量的內容將是安全的。而這正是讓管道變得如此麻煩的文字擴張。:)

它會比分支子進程和管道sed無論如何都快。你已經知道如何做整個while read line的事情,所以在Bash的現有參數擴展文檔中創造性地應用這些功能可以幫助你重現你可以用sed做的任何事情。檢查出bash手冊頁開始...