Lập trình Shell, tránh tempfiles


8

Tôi thường viết các kịch bản shell KSH theo cùng một mẫu:

  • (1) truy xuất đầu ra từ một hoặc nhiều lệnh
  • (2) định dạng nó bằng grep | cut | awk | sed và in nó ra màn hình hoặc vào một tập tin

Để làm điều đó, tôi thường lưu trữ đầu ra của (1) trong một tempfile, và sau đó thực hiện định dạng trong (2) trên tệp đó.

Lấy mã đó làm ví dụ:

TMPFILE=file.tmp

# If tmpfile exists rm it.
[ -f $TMPFILE ] && rm -f $TMPFILE

for SERVICE in $(myfunc); do
    getInfo $SERVICE > $TMPFILE # Store raw output in the TMPFILE

    # I retrieve the relevant data from the TMPFILE
    SERV_NAME=$(head -1 $TMPFILE | sed -e 's/ $//')
    SERV_HOSTNAME=$(grep HOSTNAME $TMPFILE | cut -d "=" -f2)
    SERV_ARGS=$(grep Arguments $TMPFILE | cut -d ":" -f2)

    print $SERV_NAME $SEP $SERV_HOSTNAME $SEP $SERV_ARGS
    rm -f $TMPFILE #rm the TMPFILE in vue of next iteration
done

Có cách nào, sử dụng đường ống, chuyển hướng và whatnots, để tránh ghi tệp vào đĩa mỗi lần không?

Nếu được, tôi đang sử dụng Phiên bản ksh M-11/16 / 88i


3
Đây là hình thức tốt để tránh các tên biến ALL_CAPS trong các tập lệnh shell và coi không gian tên đó được dành riêng cho shell để tránh ghi đè lên những thứ quan trọng như PATHhoặc các biến môi trường hoặc biến môi trường khác. TMPFILEcó thể tốt, nhưng TMPDIRlà đặc biệt, vì vậy bạn có thực sự muốn được đi bộ chặt chẽ đó?
jw013

Đối với hậu thế: một câu hỏi khác được đánh dấu là một bản sao của câu hỏi unix.stackexchange.com/questions/63923/ này bao gồm một câu trả lời liên quan đến ống fifo, cũng có thể được sử dụng ở đây (mặc dù đây có thể không phải là lựa chọn tốt nhất trong trường hợp cụ thể này).
goldilocks

@goldilocks: Có lẽ chúng ta có thể hợp nhất hai câu hỏi thành một. Chúng tôi có thể liên hệ với một người điều hành để làm điều này?
rahmu

@rahmu: Tôi đánh dấu câu hỏi khác. Tôi đoán nó tùy thuộc vào sức mạnh bây giờ ...
goldilocks

Câu trả lời:


9

Mã của bạn trông giống như một ví dụ hoàn toàn hợp lý về việc sử dụng tempfiles cho tôi. Tôi sẽ ở lại: gắn bó với phương pháp này. Điều duy nhất thực sự cần phải thay đổi là cách bạn tạo tempfile. Sử dụng một cái gì đó như

 TMP=$(tempfile)

hoặc là

 TMP=$(mktemp)

hoặc ít nhất

 TMP=/tmp/myscript_$$

Bằng cách này, bạn sẽ không để tên dễ dự đoán (bảo mật) và loại bỏ nhiễu quy tắc giữa một số phiên bản của tập lệnh đang chạy cùng một lúc.


2
về mặt giáo dục, trích dẫn không cần thiết cho việc gán biến.
glenn jackman

1
@glenn Đúng, trong trường hợp này, chúng không nên tạo sự khác biệt, vì mỗi lệnh thường tạo ra một chuỗi không có khoảng trắng. Nhưng đó là một thói quen tốt để có dấu ngoặc kép trong trường hợp bạn gán đầu ra lệnh cho một biến - vì vậy tôi sẽ tiếp tục để nó theo cách này.
rozcietrzewiacz

Xóa các trích dẫn trong ví dụ cuối để phân biệt.
rozcietrzewiacz

3
@roz Không, bạn đã bỏ lỡ điểm. Các phép gán biến trong shell được nhận ra trước khi thực hiện bất kỳ việc mở rộng nào và việc tách trường KHÔNG được thực hiện đối với các phép gán biến. Vì vậy, var=$(echo lots of spaces); echo "$var"là tốt và nên sản xuất lots of spacesnhư đầu ra. Sự cảnh báo thực sự không ai đề cập đến là dải thay thế tất cả các dòng mới. Đây không phải là một vấn đề ở đây và chỉ có vấn đề, ví dụ như nếu bạn gặp sự cố mktempđã tạo ra các tên tệp có dòng mới. Các công việc thông thường xung quanh, nếu cần, là var=$(echo command with trailing newline; echo x); var=${var%x}.
jw013

1
@ jw013 Vâng, tôi nhận ra điều này ngay bây giờ - không, khi tôi viết câu trả lời một năm trước. Cảm ơn đã chỉ ra điều đó! (đang sửa chữa ...)
rozcietrzewiacz

5

Bạn có thể sử dụng một biến:

info="$(getInfo $SERVICE)"
SERV_NAME="$(head -1 $TMPFILE <<<"$info" | sed -e 's/ $//')"
...

Từ man ksh:

<<<word       A  short  form of here document in which word becomes the
              contents of the here-document after any parameter  expan-
              sion,  command  substitution, and arithmetic substitution
              occur.

Ưu điểm bao gồm:

  • Cho phép thực hiện song song.
  • Theo kinh nghiệm của tôi, đây là tấn nhanh hơn các tập tin tạm thời. Trừ khi bạn có quá nhiều dữ liệu mà cuối cùng bạn phải trao đổi, đó sẽ là các đơn đặt hàng có cường độ nhanh hơn (chỉ chặn các bộ đệm bộ đệm HD, có thể nhanh như vậy đối với số lượng dữ liệu nhỏ).
  • Các quy trình hoặc người dùng khác không thể làm xáo trộn dữ liệu của bạn.

<<< dường như không tồn tại trong ksh của tôi. Tôi gặp một lỗi và dường như tôi không thể tìm thấy nó trong trang man. Tôi đang sử dụng ksh88. Bạn có chắc chắn phiên bản này nên có tính năng này?
rahmu

Không; Tôi đoán tôi đã không kiểm tra đúng mantrang (không có đề cập đến số phiên bản trên trang web: /)
l0b0

<<<bash 'đây chuỗi'. Tôi không nghĩ rằng nó xuất hiện trong bất kỳ vỏ khác. (Ồ, zshcó lẽ ...)
rozcietrzewiacz

2
@rozcietrzewiacz: Google cho man ksh. Nó chắc chắn đã được đề cập ở đó.
l0b0

3
Đoán làm thế nào bash thực hiện ở đây-chuỗi và đây-docs. sleep 3 <<<"here string" & lsof -p $! | grep 0rsleep 30251 anthony 0r REG 253,0 12 263271 /tmp/sh-thd-7256597168 (deleted)- vâng, nó sử dụng tempfile.
derobert

2

Bạn có hai lựa chọn:

  1. Bạn lấy dữ liệu một lần (trong ví dụ của bạn với getInfo) và lưu trữ nó trong một tệp như bạn làm.

  2. Bạn tìm nạp dữ liệu mỗi lần và không lưu trữ cục bộ, tức là bạn gọi getInfomỗi lần

Tôi không thấy vấn đề trong việc tạo một tệp tạm thời để tránh xử lý lại / tìm nạp lại.

Nếu bạn lo lắng về việc để lại tệp tạm thời ở đâu đó, bạn luôn có thể sử dụng trapđể chắc chắn xóa nó trong trường hợp tập lệnh bị tắt / bị gián đoạn

trap "rm -f $TMPFILE" EXIT HUP INT QUIT TERM

và sử dụng mktempđể tạo một tên tệp duy nhất cho tệp tạm thời của bạn.


1

Thay vì tạo một tệp, hãy xây dựng các câu lệnh gán shell và đánh giá đầu ra đó.

for SERVICE in $(myfunc); do
    eval $(getInfo $SERVICE |
               sed -n -e '1/\(.*\) *$/SERV_NAME="\1"/p' \
                   -e '/HOSTNAME/s/^[^=]*=\([^=]*\).*/SERV_HOSTNAME="\1"/p' \
                   -e '/Arguments/^[^:]*:\([^:]*\).*/SERV_ARGS="\1"/p')
    print $SERV_NAME $SEP $SERV_HOSTNAME $SED $SERV_ARGS
done

Hoặc nếu bạn chỉ muốn in thông tin:

for SERVICE in $(myfunc); do
    getInfo $SERVICE | awk -vsep="$SEP" '
        BEGIN{OFS=sep}
        NR == 1 { sub(/ *$/,""); SERV_NAME=$0 }
        /HOSTNAME/ { split($0, HOST, /=/; SERV_HOSTNAME=HOST[2]; }
        /Arguments/ { split($0, ARGS, /:/; SERV_ARGS }
        END { print SERV_NAME, SERV_HOSTNAME, SERV_ARGS }'
done
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.