2010-01-22 39 views
6

tôi cần phải viết một kịch bản bash sẽ lặp thông qua các nội dung của một thư mục (bao gồm cả thư mục con) và thực hiện việc thay thế sau:bash scripting thách thức

  • thay thế 'foo' trong bất kỳ tên tập tin với 'bar'
  • thay thế 'foo' trong các nội dung của bất kỳ tập tin với 'bar'

Cho đến nay tất cả tôi nhận được là

find . -name '*' -exec {} \; 

:-)

+6

Bởi vì cha mẹ tôi không cho tôi đủ tình cảm như một đứa trẻ –

+0

ahaha Tôi sẽ cho bạn +1 chỉ để nhận xét đó. Bây giờ, đây là bài tập về nhà? –

+0

Không, nó không phải là bài tập về nhà. Nếu bạn nhìn vào hồ sơ của tôi nó (hy vọng) rõ ràng rằng tôi không phải là sinh viên. –

Trả lời

2

Với RH rename:

find -f \(-exec sed -i s/foo/bar/g \; , -name \*foo\* -exec rename foo bar {} \; \) 
2
find "[email protected]" -depth -exec sed -i -e s/foo/bar/g {} \; , -name '*foo*' -print0 | 
while read -d '' file; do 
    base=$(basename "$file") 
    mv "$file" "$(dirname "$file")/${base//foo/bar}" 
done 
+0

Đáng buồn thay, toán tử ',' không phải là tiêu chuẩn, và chỉ xuất hiện trong GNU 'find'. Ngoài ra, điều này chuyển các thư mục cũng như các tệp vào 'sed', bạn cần thêm một' -type f' để tránh lỗi. Tôi thích nó vì nó gọn gàng hơn hầu hết các câu trả lời khác ở đây. –

+0

Vâng, 'sed' sẽ lỗi trên một thư mục nhưng' tìm' sẽ tiếp tục ;-) Không có ',' Tôi nghĩ rằng điều này sẽ phải được chia thành hai lời gọi 'tìm' hoặc làm cho phần Bash hoạt động nhiều hơn. – ephemient

1

CẬP NHẬT: 1632 EST

  • Bây giờ xử lý khoảng trắng nhưng 'trong khi đọc mục' không bao giờ chấm dứt. Tốt hơn, nhưng vẫn không đúng. Sẽ giữ làm việc về điều này.

aj @ mmdev0: ~/foo_to_bar $ cat script.sh

#!/bin/bash 

dirty=true 
while ${dirty} 
do 
    find ./ -name "*" |sed -s 's/ /\ /g'|while read item 
    do 
     if [[ ${item} == "./script.sh" ]] 
     then 
      continue 
     fi 
     echo "working on: ${item}" 

     if [[ ${item} == *foo* ]] 
     then 
      rename 's/foo/bar/' "${item}" 
      dirty=true 
      break 
     fi 

     if [[ ! -d ${item} ]] 
     then 
      cat "${item}" |sed -e 's/foo/bar/g' > "${item}".sed; mv "${item}".sed "${item}" 
     fi 
     dirty=false 
    done 
done 
+0

'cho mục trong \' ... \ '' làm điều sai nếu bất kỳ tên tập tin nào chứa khoảng trắng. – ephemient

+0

@ephemient đúng bạn đang, đang khắc phục sự cố này ngay bây giờ ... –

1
#!/bin/bash 
function RecurseDirs 
{ 
oldIFS=$IFS 
IFS=$'\n' 
for f in * 
do 
    if [[ -f "${f}" ]]; then 
    newf=`echo "${f}" | sed -e 's/foo/bar/g'` 
    sed -e 's/foo/bar/g' < "${f}" > "${newf}" 
    fi 
    if [[ -d "${f}" && "${f}" != '.' && "${f}" != '..' && ! -L "${f}" ]]; then 
    cd "${f}" 
    RecurseDirs . 
    cd .. 
    fi 
done 
IFS=$oldIFS 
} 
RecurseDirs . 
+0

ngôn ngữ rất tốt đẹp, đáng thương :) –

+0

@Don: Âm thanh giống như lỗi sao chép và dán. Thay vào đó, hãy thử nhập mục này. @dreynold: xem ra cho các liên kết tượng trưng, ​​bạn có thể gặp khó khăn trong các vòng lặp thư mục hoặc đi đến một nơi khác hoàn toàn như '..' có thể không phải là những gì bạn nghĩ. (Có, Bash thường cố gắng sửa lỗi này, nhưng đó là một heuristic. Xem http://stackoverflow.com/questions/2105572) – ephemient

+0

@ephemient: Có, bạn là chính xác - đây không phải là điều an toàn nhất xung quanh. Đúng vậy, tôi nghĩ vậy. – dreynold

0

bash 4,0

#!/bin/bash 
shopt -s globstar 
path="/path" 
cd $path 
for file in ** 
do 
    if [ -d "$file" ] && [[ "$file" =~ ".*foo.*" ]];then 
     echo mv "$file" "${file//foo/bar}" 
    elif [ -f "$file" ];then 
     while read -r line 
     do 
      case "$line" in 
       *foo*) line="${line//foo/bar}";; 
      esac 
      echo "$line" 
     done < "$file" > temp 
     echo mv temp "$file" 

    fi 
done 

loại bỏ các 'echo' để thực hiện những thay đổi

+0

Nit nhỏ: nếu '$ line' là' '-e'' hoặc '' -n'' hoặc một số công tắc khác mà Bash' echo' nhận ra, nó sẽ bị xáo trộn trong tệp. Không có hại trong việc thực hiện vô điều kiện sự thay thế, vì vậy tôi sẽ cắt toàn bộ đó trong khi cơ thể xuống 'printf '% s \ n'" $ {line // foo/bar} "'. Một nit khác: bạn đang sử dụng kiểm tra ngoặc kép của Bash rồi, tại sao không viết ngắn hơn '[[$ file = * foo *]]'? Bạn cũng có các vấn đề khó khắc phục hơn khi một tên dir cấp cao thay đổi và bạn đang làm việc trên cái gì đó sâu hơn vào cây thư mục và không đổi tên các thư mục không (điều này sẽ xảy ra, bằng cách đọc câu hỏi của OP). – ephemient

0

for f in `tree -fi | grep foo`; do sed -i -e 's/foo/bar/g' $f ; done

0

Tuy nhiên, một giải pháp tìm-exec:

find . -type f -exec bash -c ' 
path="{}"; 
dirName="${path%/*}"; 
baseName="${path##*/}"; 
nbaseName="${baseName/foo/bar}"; 
#nbaseName="${baseName//foo/bar}"; 
# cf. http://www.bash-hackers.org/wiki/doku.php?id=howto:edit-ed 
ed -s "${path}" <<< $'H\ng/foo/s/foo/bar/g\nwq'; 
#sed -i "" -e 's/foo/bar/g' "${path}"; # alternative for large files 
exec mv -iv "{}" "${dirName}/${nbaseName}" 
' \; 
+0

Trong trường hợp tên tệp có chứa "hoặc \, bạn nên sử dụng một cái gì đó giống như đường dẫn' -exec bash -c '= $ 1; ...' - {} \; '. – ephemient

0

chỉnh để tìm-exec cách tiếp cận bởi gregb (thêm dấu ngoặc kép):

# compare 

bash -c ' 
    echo $'a\nb\nc' 
' 

bash -c ' 
    echo $'"'a\nb\nc'"' 
' 

# therefore we need 

find . -type f -exec bash -c ' 
... 
ed -s "${path}" <<< $'"'H\ng/foo/s/foo/bar/g\nwq'"'; 
... 
' \; 
Các vấn đề liên quan