2015-02-07 54 views
-1

請幫我以下的問題:變化符合腳本(Python或bash)的

(1)我需要找到一個文件夾中的所有* .txt文件夾及其子 ( 2)在這些純文本文件中,有一行以「#GAP:6009,27」開頭。我想在所有文件中改變這個值,減少175。例如:之前:「#GAP:6009,27」,之後:「#GAP:5834,27」。

文本文件包含很多行,只有特定的行應該更改。例如:

[...] 
#MedleyEndBeat:790 
#BPM:335,53 
#GAP:6009,27 
#ENCODING:CP1252 
[...] 

我該如何做到這一點?

我的想法:

找到所有的txt文件,並運行Python腳本

find musicdir -name \*.txt -exec ./mypythonscript {} \;" 

Python腳本包含像這樣(只是想法,語法是完全錯誤的,我知道,但我會看它後來):

filepath = open($1) 
with open(filepath) as file: 
    for line in file: 
     if line[0:5] == "#GAP:" then 
      newvalue = calc(line[6:]-175) # extract substring (the GAP value till end of line) 
      newfile += "#GAP:" + newvalue 
      GAP_FOUND = 1 
     else 
      newfile += line 
write(filepath) << newfile 
if GAP_FOUND != 1 then 
    echo "ERROR: GAP LINE NOT FOUND IN"+filepath 

那麼有沒有更好的方法來做到這一點?或者可以這樣做?我不是專業的編碼器,這就是爲什麼我的方法可能看起來不好:)

+0

有很多方法可以做到這一點,但如果你已經知道python,那麼這看起來很好。就我個人而言,我會製作一個更改後的文件的副本,只有當你知道內容全部寫完,但可能比你需要的更偏執狂 – 2015-02-07 08:52:45

+0

你走在正確的軌道上,但你絕對可以更優雅。你可以讓python腳本自己找到所有的文件。您可以使用多個上下文管理器一次打開多個文件,並且可以更有效地利用內存。 Gap_found應該只是一個布爾值。如果你想超級漂亮,那麼你可以使用線程或多處理來做得更快。 – 2015-02-07 09:09:00

+0

謝謝大家!我學到了很多。很多不同的方法來解決這個問題=) – merlin 2015-02-07 16:34:20

回答

0

這是所有可行的純粹在Python,但我懷疑你可以把它作爲如果這是對雅緻的可接受度量,那麼它就相當於相應的shell腳本。

find . -name '*.txt' -print0 | 
xargs -0 sh -c 'for file; do 
    awk -F "[:,]" "/^#GAP:/ { 
     g=\$2-175; o=\":\" \$2 \","; sub(o, \":\" g ","); x=1 } 1 
    END { exit 1-x }" "$file" >"$file.tmp" && 
    mv "$file.tmp" "$file" || rm "$file.tmp"; done' _ 

find產生匹配文件名的列表。sh腳本在這些文件上循環(最後參數_有點瑕疵; sh -c 'script...'之後的第一個參數是在腳本中使用argv[0]的值),並且Awk腳本查找匹配併產生退出代碼這表明它是否被發現。在成功的情況下,生成的臨時輸出文件將移到輸入文件的頂部;否則,臨時文件將被丟棄(以免更改內容未更改的文件)。

如果文件中的分隔符是統一的,Awk腳本可能會簡單得多。事實上,我不得不強制一些東西,所以這個劇本的主體根本不是很優雅。

0

我剛剛寫了一個小的bash腳本,實際上比做洞工作。你不需要python來實現這一點。這只是一個基本的破解,我已經在Mac OS X和Ubuntu上進行了測試。在其他版本的Linux/Unix上可能會有一些問題。所以在運行之前在你的系統上進行測試。

我也爲代碼添加了一些評論,所以你可以看到並學習會發生什麼。

#!/bin/bash 
# the dir that will be searched 
musicdir="/home/user/music" 
# amount to deduct from GAP 
subamount=127 

new_gap() 
{ 
    # replace "," by "." 
    line=`echo $1|sed 's/,/\\./g'` 
    # deduct 
    nv=`bc -l <<< "scale=2; $line-$subamount"` 
    # replace "." by "," 
    line=`echo $nv|sed 's/\\./,/g'` 
    # return new value 
    echo $line 
} 

for file in `find $musicdir -iname "*.txt"`; do 
    while read p; do 
     # regex to match the #GAP: line 
     GAP=`[[ $p =~ ^"#GAP:"(.*)$ ]] && echo ${BASH_REMATCH[1]}` 
     # if we found the wanted line, we process it 
     if [ ! -z "$GAP" ]; then 
      # the value of the #GAP: line is now in the var $GAP (without the start: #GAP:) 
      # we pass it to the function "new_gap" so there can be a new value calculated 
      # and returned (echo). the new line will then saved in the file 
      echo "#GAP:"$(new_gap $GAP) 
     # if it is a normal line, just write it back to the file 
     else 
      echo $p 
     fi 
    done <$file>$file.new 
    # now be make a backup of the original file 
    mv "$file" "$file.back" 
    # and copy the new file to the original location 
    mv "$file.new" "$file" 
done 
0

此致對於fileinput模塊,該標準輸入從文件名的列表連接到文件的內容,以及任選的作業,通過使用inplace開關的重定向標準輸出到相同的文件。

$ cat decrement.py 
import fileinput 
import sys 

start = sys.argv[1] ; l = len(start) 
delta = int(sys.argv[2]) 
files = sys.argv[3:] 

for line in fileinput.input(files, inplace=True): 
    if line.startswith(start): 
     items = line[l:].split(',') 
     items[0] = str(int(items[0])-delta) 
     print(start+','.join(items), end='') 
    else: 
     print(line, end='') 
$ python3 decrement.py "#GAP:" 185 `find musicdir -name \*.txt` 

請注意,我們不得不引用#GAP:,因爲散列是大多數shell的註釋字符。

雖然腳本使用print,但是沒有輸出產生到stdout,因爲fileinputstdout映射到被覆蓋的文件。實際上,臨時文件用於最大限度地減少與不完整運行相關的風險,但這對程序員來說是完全透明的。

如果你願意使用python2,使用print line,帶有尾隨逗號,以避免在輸出空白行...

+0

非常感謝。有一個小問題,您的腳本只接受UTF8文件,否則會出現錯誤,例如「UnicodeDecodeError:'utf-8'編解碼器無法解碼字節0xe4的位置:無效的連續字節ISO-8859」。所有文件的編碼是不同的。將所有文件轉換爲utf-8後,我工作得很好。謝謝! – merlin 2015-02-07 16:32:34