Làm thế nào để cat << EOF >> một tệp chứa mã?


100

Tôi muốn in mã vào một tệp bằng cách sử dụng cat <<EOF >>:

cat <<EOF >> brightup.sh
!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

nhưng khi tôi kiểm tra đầu ra tệp, tôi nhận được điều này:

!/bin/bash
curr=1634
if [  -lt 4477 ]; then
   curr=406;
   echo   > /sys/class/backlight/intel_backlight/brightness;
fi

Tôi đã thử đặt các dấu nháy đơn nhưng đầu ra cũng mang các dấu nháy đơn với nó. Làm cách nào để tránh vấn đề này?


2
Bạn cũng nên sửa chữa shebang. Dòng đầu tiên cần phải có nghĩa đen #!/bin/bashvà không có gì khác - đó #!là thứ làm cho nó trở thành một dòng shebang hợp lệ và những gì xuất hiện sau nó là đường dẫn đến trình thông dịch.
tripleee lúc

1
xem man bashvà tìm kiếm Here Documents. Tất cả các chi tiết có.
Hong

1
Ngoài ra, cú pháp hiện đại để thay thế quy trình là $(command)thay thế `command`. Đối với việc thu thập các nội dung của một tập tin, Bash có$(<file)
tripleee

Câu trả lời:


158

Bạn chỉ cần một thay đổi tối thiểu; trích dẫn đơn dấu phân cách ở đây tài liệu sau <<.

cat <<'EOF' >> brightup.sh

hoặc dấu gạch chéo ngược tương đương - thoát khỏi nó:

cat <<\EOF >>brightup.sh

Nếu không có trích dẫn, tài liệu ở đây sẽ trải qua quá trình thay thế biến, dấu gạch ngược sẽ được đánh giá, v.v., giống như bạn đã khám phá.

Nếu bạn cần mở rộng một số, nhưng không phải tất cả, giá trị, bạn cần phải thoát khỏi những giá trị bạn muốn ngăn chặn.

cat <<EOF >>brightup.sh
#!/bin/sh
# Created on $(date # : <<-- this will be evaluated before cat;)
echo "\$HOME will not be evaluated because it is backslash-escaped"
EOF

sẽ sản xuất

#!/bin/sh
# Created on Fri Feb 16 11:00:18 UTC 2018
echo "$HOME will not be evaluated because it is backslash-escaped"

Theo đề xuất của @fedorqui , đây là phần có liên quan từ man bash:

Tài liệu đây

Loại chuyển hướng này hướng dẫn shell đọc đầu vào từ nguồn hiện tại cho đến khi thấy một dòng chỉ chứa dấu phân cách (không có khoảng trống ở cuối). Tất cả các dòng được đọc đến thời điểm đó sau đó được sử dụng làm đầu vào chuẩn cho một lệnh.

Định dạng của tài liệu ở đây là:

      <<[-]word
              here-document
      delimiter

Không có mở rộng tham số, thay thế lệnh, mở rộng số học hoặc mở rộng tên đường dẫn được thực hiện trên word. Nếu bất kỳ ký tự nào trong word được trích dẫn, dấu phân cách là kết quả của việc xóa trích dẫn trên word và các dòng trong tài liệu ở đây không được mở rộng. Nếu từ không được trích dẫn, tất cả các dòng của tài liệu ở đây phải chịu sự mở rộng tham số, thay thế lệnh và mở rộng số học . Trong trường hợp thứ hai, chuỗi ký tự \ bị bỏ qua và \ phải được sử dụng để trích dẫn các ký tự \, $ và `.


1
Tôi thấy bạn được đánh dấu Làm thế nào để tránh các biến mở rộng heredoc? như một bản sao của cái này. Không phản đối, chỉ là tôi sẽ bao gồm tài liệu tham khảo Bash, như tôi đã làm trong tài liệu của tôi (chỉ có 13k lượt truy cập đã có gần 100 đại diện, vì vậy nó có vẻ khá hữu ích).
fedorqui 'SO dừng hại'

@fedorqui Có thể bạn thực sự muốn chuyển câu trả lời của mình cho câu hỏi này? Hoặc chúng ta có thể chuyển đánh dấu trùng lặp sang cách khác; Tôi chỉ nhìn vào điểm câu hỏi, không nhiều điểm câu trả lời.
tripleee

Mmm thì sao về việc hợp nhất chúng, lấy cái này làm câu hỏi mục tiêu? Câu hỏi trùng lặp có một tiêu đề hay, chỉ có điều là nó quá dài. Tuy nhiên, bằng cách nào đó, nó đã nhận được khá nhiều sự chú ý từ muộn (tôi đang nhận được một số lượt ủng hộ mỗi tháng ).
fedorqui 'SO dừng hại'

@fedorqui Tôi thích khái niệm hợp nhất nhưng tôi chưa bao giờ thấy nó xảy ra trong thực tế; Nếu tôi hiểu đúng tình huống, các mod đơn giản là không thể xử lý các hợp nhất tầm thường vì sự phức tạp và phức tạp vượt xa lợi ích. Những gì tôi đã làm một hoặc hai lần là xóa một câu trả lời hữu ích và được ủng hộ và đăng lại nó dưới một câu hỏi khác, mặc dù tôi khó có thể đề xuất cách giải quyết này.
tripleee

Thật khó để biết khi nào việc đó là đáng làm. Tôi đã làm điều đó trong một trang web mà tôi sửa đổi khi một câu hỏi hay được đăng mà không nhận thấy rằng có một câu hỏi hay khác từ trước đó, cả hai đều có câu trả lời tốt. Loại bỏ câu trả lời trên 90 điểm của tôi có vẻ không phải là một kế hoạch hay, vì nó sẽ khiến câu hỏi đó trở thành trẻ mồ côi.
fedorqui 'SO dừng hại'

20

Hoặc, bằng cách sử dụng điểm đánh dấu EOF của bạn, bạn cần phải trích dẫn điểm đánh dấu ban đầu để việc mở rộng sẽ không được thực hiện:

#-----v---v------
cat <<'EOF' >> brightup.sh
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

IHTH


1
Tôi sẽ chỉnh sửa bài đăng của bạn nhưng chỉ để an toàn, nó không phải là #! / Bin / bash và không! / Bin / bash?
Matthew Hoggan

@MatthewHoggan: Đúng vậy, bạn nói đúng! Cảm ơn vì đã nắm bắt được điều đó. Tôi đang sửa nó bây giờ.
shellter

16

Điều này sẽ hoạt động, tôi chỉ thử nghiệm nó ra và nó hoạt động như mong đợi: không có sự mở rộng, thay thế hoặc những gì đã xảy ra.

cat <<< '
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
  curr=$((curr+406));
  echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi' > file # use overwrite mode so that you don't keep on appending the same script to that file over and over again, unless that's what you want. 

Sử dụng những điều sau đây cũng hoạt động.

cat <<< ' > file
 ... code ...'

Ngoài ra, cần lưu ý rằng khi sử dụng heredocs, chẳng hạn như << EOFthay thế và mở rộng biến và những điều tương tự diễn ra. Vì vậy, làm một cái gì đó như thế này:

cat << EOF > file
cd "$HOME"
echo "$PWD" # echo the current path
EOF

sẽ luôn dẫn đến việc mở rộng các biến $HOME$PWD. Vì vậy, nếu thư mục chính của bạn là /home/foobarvà đường dẫn hiện tại /home/foobar/bin, filesẽ giống như sau:

cd "/home/foobar"
echo "/home/foobar/bin"

thay vì dự kiến:

cd "$HOME"
echo "$PWD"

2
Một chuỗi ở đây trong dấu ngoặc kép rõ ràng không thể chứa bất kỳ dấu ngoặc kép nào, đó có thể là một vấn đề nghiêm trọng. Ở đây, các tài liệu là giải pháp lành mạnh duy nhất nếu bạn có một tập lệnh cần chứa cả dấu nháy đơn và dấu ngoặc kép, mặc dù tất nhiên điều đó không đúng với ví dụ đơn giản của OP. Ngoài ra, theo phương pháp tiếp tuyến, các chuỗi ở đây <<<chỉ có sẵn bắt đầu từ Bash 3 và không khả dụng với các trình bao khác.
tripleee

<<<cũng có sẵn trong Zsh
Alexej Magura.

3
hôm nay tôi được biết rằng bạn có thể có tên tệp ngay sau khi mở heredoc. Cảm ơn @AlexejMagura!
chaseadamsio
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.