bash script - chức năng lặp


7

Tôi quản lý để viết kịch bản sau đây:

#!/bin/bash

#files list
file1=/tmp/1wall_long.txt
file2=/tmp/1wall_test1.txt
file3=/tmp/1wall_test2.txt
file4=/tmp/1wall_test3.txt
file5=/tmp/3mt_long.txt
file6=/tmp/3mt_OpenSpace_test1.txt
file7=/tmp/3mt_OpenSpace_test2.txt
file8=/tmp/3mt_OpenSpace_test3.txt
file9=/tmp/3rooms_test1.txt
file10=/tmp/3rooms_test2.txt
file11=/tmp/3rooms_test3.txt
file12=/tmp/20mt_OpenSpace_test1.txt
file13=/tmp/20mt_OpenSpace_test2.txt
file14=/tmp/20mt_OpenSpace_test3.txt

#script for 1wall_long file
if [ ! -e "$file1" ]; then #check if the file exist
    echo "File 1wall_long.txt does not exist" #if not exist print echo output
else
    sed -i -e 's/- /-/g' $file1 #remove space on the first 10 values
    awk '{print $7}' $file1 > /tmp/1wall_long_S.txt #print the column number 7 and copy the output in a file
    rm $file1 #remove old file
fi

Tập lệnh được lặp lại cho tất cả các tệp được mô tả trong biến (về cơ bản tôi có cùng một tập lệnh được lặp lại 14 lần với các biến khác nhau) Có cách nào tốt hơn để làm điều đó không và cách thực hành tốt nhất trong những tình huống này là gì?


1
Điều này có lẽ nên được di chuyển đến codereview.stackexchange.com
Ethan Bierlein

1
@EthanBierlein không, không nên. Chúng tôi không bao giờ di chuyển các câu hỏi chủ đề trừ khi OP yêu cầu. Bash scripting rất nhiều về chủ đề ở đây, vì vậy câu hỏi này được chào đón ở lại.
terdon

Câu trả lời:


6

Cá nhân, tôi sẽ tránh mã hóa tên tập tin. Điều đó hiếm khi là một ý tưởng tốt và thường tốt hơn là có tùy chọn chuyển các tệp mục tiêu làm đối số. Ngoài ra, bạn đang sửa đổi tệp tại chỗ và sau đó xóa bản gốc. Điều đó không hiệu quả, chỉ cần sửa đổi tệp nhanh chóng và in cột thứ 7 mà không phải ghi nó vào đĩa. Ví dụ:

#!/usr/bin/env bash

## Iterate over the file names given
for file in "$@"; do
    ## Get the output file's name. The ${file%.*} is
    ## the file's anme without its extension.
    outfile="${file%.*}"_S.txt
    ## If the file exists
    if [ -e "$file" ]; then
    ## remove the spaces and print the 7th column
    sed 's/- /-/g' "$file" | awk '{print $7}' > "$outfile" &&
        ## Delete the original but only if the step
        ## above was successful (that's what the && does)/
        rm "$file" 
    else
    ## If the file doesn't exist, print an error message
    echo "The file $file does not exist!"
    fi
done

Sau đó, bạn có thể chạy tập lệnh như thế này:

foo.sh /tmp/1wall_long.txt /tmp/1wall_test1.txt /tmp/1wall_test2.txt /tmp/1wall_test3.txt /tmp/20mt_OpenSpace_test1.txt /tmp/20mt_OpenSpace_test2.txt /tmp/20mt_OpenSpace_test3.txt /tmp/3mt_long.txt /tmp/3mt_OpenSpace_test1.txt /tmp/3mt_OpenSpace_test2.txt /tmp/3mt_OpenSpace_test3.txt /tmp/3rooms_test1.txt /tmp/3rooms_test2.txt /tmp/3rooms_test3.txt 

Nếu bạn muốn mã hóa tên cứng, chỉ cần sử dụng một mảng theo đề xuất của @choroba:

#!/usr/bin/env bash

files=(/tmp/1wall_long.txt /tmp/1wall_test1.txt /tmp/1wall_test2.txt /tmp/1wall_test3.txt /tmp/20mt_OpenSpace_test1.txt /tmp/20mt_OpenSpace_test2.txt /tmp/20mt_OpenSpace_test3.txt /tmp/3mt_long.txt /tmp/3mt_OpenSpace_test1.txt /tmp/3mt_OpenSpace_test2.txt /tmp/3mt_OpenSpace_test3.txt /tmp/3rooms_test1.txt /tmp/3rooms_test2.txt /tmp/3rooms_test3.txt )


## Iterate over the file names given
for file in "${files[@]}"; do
    ## Get the output file's name. The ${file%.*} is
    ## the file's anme without its extension.
    outfile="${file%.*}"_S.txt
    ## If the file exists
    if [ -e "$file" ]; then
    ## remove the spaces and print the 7th column
    sed 's/- /-/g' "$file" | awk '{print $7}' > "$outfile" &&
        ## Delete the original but only if the step
        ## above was successful (that's what the && does)/
        rm "$file" 
    else
    ## If the file doesn't exist, print an error message
    echo "The file $file does not exist!"
    fi
done

Xin chào, cảm ơn bạn đã trả lời. Nghe có vẻ tương tự như trả lời được đưa ra bởi choroba. Đúng không?
Federi

Cái thứ hai là, vâng. Sự khác biệt chính là tôi tránh tạo một tệp chỉ để xóa nó sau này. Bạn sed -ichỉ cần thêm công việc. Vì dù sao bạn cũng sẽ xóa tệp, không có lý do gì để sử dụng -imà sẽ chỉ thêm thao tác ghi không cần thiết vào đĩa. Đề nghị đầu tiên của tôi là khác nhau: thay vì viết tên tệp trong tập lệnh, hãy chuyển chúng dưới dạng đối số. Bằng cách đó, bạn có thể dễ dàng thay đổi các tệp mà nó chạy.
terdon

Xin lỗi, cuối cùng. Ý nghĩa của% trong "$ {file%. *}"
Federi

1
@Federi, ${var%pattern}trả về giá trị $varvới patternloại bỏ từ cuối cùng. Vì vậy, ${file%.*}sẽ loại bỏ dấu chấm cuối cùng và mọi thứ sau đó. (Lưu ý: foo.bar.bazsẽ trở thành foo.bar. Nếu bạn sử dụng %%, nó sẽ trở thành foo.)
nyuszika7h

8

không vòng lặp

đầu tiên sử dụng một chức năng

function sevenc
{


if [ ! -e "$1" ]; then #check if the file exist
    echo "File $1 does not exist" #if not exist print echo output
else
    sed -i -e 's/- /-/g' "$1" #remove space on the first 10 values
    awk '{print $7}' "$1" > /tmp/$(basename $1.txt)_S.txt #print the column number 7 and copy the output in a file
    rm "$1"  #remove old file
fi
}
  • khi shell nhận ra một hàm, nó sẽ chuyển đối số (nếu có thành $ 1 $ 2 ... và cứ thế).
  • nhân tiện

's / - / - / g' "$ 1" #remove khoảng trống trên 10 giá trị đầu tiên

KHÔNG, nó biến tất cả space-thành -dòng, ở đó 1, 4, 10 hoặc 255.

sau đó không cần thêm var

sevenc /tmp/1wall_long.txt
sevenc /tmp/1wall_test1.txt
sevenc /tmp/1wall_test2.txt
sevenc /tmp/1wall_test3.txt
sevenc /tmp/3mt_long.txt
sevenc /tmp/3mt_OpenSpace_test1.txt
sevenc /tmp/3mt_OpenSpace_test2.txt
sevenc /tmp/3mt_OpenSpace_test3.txt
sevenc /tmp/3rooms_test1.txt
sevenc /tmp/3rooms_test2.txt
sevenc /tmp/3rooms_test3.txt
sevenc /tmp/20mt_OpenSpace_test1.txt
sevenc /tmp/20mt_OpenSpace_test2.txt
sevenc /tmp/20mt_OpenSpace_test3.txt

(miễn là bạn không sử dụng fileXX var nữa).

không vòng lặp (sol. 2)

nếu bạn muốn vượt qua nhiều đối số hơn và sử dụng thử tối ưu hóa của Terdon

function eight
{

file=$1
destdir=${2-/tmp} # use second arg if defined, else /tmp
exten=${3-S} 

if [ ! -e "$file" ]; then #check if the file exist
    echo "File $file does not exist" #if not exist print echo output
else
    sed  -e 's/- /-/g' "$file" \
    awk '{print $7}' "$1" > /"$destdir"/$(basename $1.txt)_"$exten".txt #print the column number 7 and copy the output in a file
    rm "$file"  #remove old file
fi
}

được gọi với

eight /tmp/1wall_test3.txt /my/projec/dir T ## will use /my/project/dir as dit, T as extension
eight /tmp/1wall_test1.txt /my/project ## will use /my/project as dir
eignt /tmp/1wall_test2.txt ## will use default value

những hàm này có thể được định nghĩa trong .bashrc và được sử dụng tương tác.

với vòng lặp

while read f
do
if [ ! -e "$f" ]; then #check if the file exist
    echo "File $1 does not exist" #if not exist print echo output
else
    sed -i -e 's/- /-/g' "$f" #remove space on the first 10 values
    awk '{print $7}' "$f" > "/tmp/$(basename $f .txt)_S.txt" #print the column number 7 and copy the output in a file
    rm "$f"  #remove old file
fi
done <<EOF
/tmp/1wall_long.txt
/tmp/1wall_test1.txt
/tmp/1wall_test2.txt
/tmp/1wall_test3.txt
/tmp/3mt_long.txt
/tmp/3mt_OpenSpace_test1.txt
/tmp/3mt_OpenSpace_test2.txt
/tmp/3mt_OpenSpace_test3.txt
/tmp/3rooms_test1.txt
/tmp/3rooms_test2.txt
/tmp/3rooms_test3.txt
/tmp/20mt_OpenSpace_test1.txt
/tmp/20mt_OpenSpace_test2.txt
/tmp/20mt_OpenSpace_test3.txt
EOF

Cảm ơn vì đã trả lời. Tôi có một số câu hỏi tùy chọn "looples": 1. Tôi có phải xác định biến $ 1 không? 2. Tên Sevenc tôi đoán là một ví dụ và tôi có thể sử dụng tên nào tôi thích 3. Làm thế nào tôi có thể thay đổi tên cơ sở cho mỗi đầu ra?
Federi

xem chỉnh sửa cho sol 2.
Archemar

6

Bạn có thể sử dụng một mảng các tệp và lặp qua nó với for:

#!/bin/bash

files=(/tmp/1wall_long.txt
       /tmp/1wall_test1.txt
       /tmp/1wall_test2.txt
       /tmp/1wall_test3.txt
       /tmp/3mt_long.txt
       /tmp/3mt_OpenSpace_test1.txt
       /tmp/3mt_OpenSpace_test2.txt
       /tmp/3mt_OpenSpace_test3.txt
       /tmp/3rooms_test1.txt
       /tmp/3rooms_test2.txt
       /tmp/3rooms_test3.txt
       /tmp/20mt_OpenSpace_test1.txt
       /tmp/20mt_OpenSpace_test2.txt
       /tmp/20mt_OpenSpace_test3.txt )

for file in "${files[@]}" ; do
    if [ ! -e "$file" ]; then
        echo "File $file does not exist"
    else
        sed -i -e 's/- /-/g' "$file"
        # Use parameter expansion to create the new file name.
        newfile=${file%.txt}_S.txt
        awk '{print $7}' "$file" > "$newfile"
        rm "$file"
    fi
done

1
Điều đó nghe có vẻ quen thuộc hơn đối với trình độ hiểu biết thấp của tôi :) Cảm ơn Bạn Chỉ cần một câu hỏi nhanh, ý nghĩa của @ trong $ {files [@]} là gì?
Federi

2
$ {files [@]} cung cấp cho bạn toàn bộ mảng, vì $ {files} chỉ cung cấp cho bạn phần tử đầu tiên.
rexkogitans
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.