Tại sao printf in nhiều đối số hơn dự kiến?


9

Tại sao shell script in đầu vào hai lần?

Tôi dự kiến ​​kịch bản sẽ bỏ qua các đầu vào sau 5.

Kịch bản:

#! /bin/bash
echo "Enter 5 words : "
read  a b c d e 
printf "> %s %s %s %s %s <" $a $b $c $d $e

Đầu ra:

user@linux:~$ pico ifs2.sh
user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 
> 1 2 3 4 5 <user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 6
> 1 2 3 4 5 <> 6     <user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 6 7 8 9 0
> 1 2 3 4 5 <> 6 7 8 9 0 <user@linux:~$ 

Và, tập lệnh sau hoạt động bất kể cái gì được đặt thành $ IFS. Tại sao?

#! /bin/bash    
old="$IFS"
IFS=":"
echo "IFS = $IFS"
echo "Enter 5 words : "
read  a b c d e 
printf "> %s %s %s %s %s <" $a $b $c $d $e    
IFS="$old"

Đầu ra:

user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1 2 3 4 5  
> 1 2 3 4 5      <user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1 2 3 4 5
> 1 2 3 4 5     <user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1:2:3:4:5
> 1 2 3 4 5 <user@linux:~$ 

dừng lại printfbất cứ lúc nào với \clối thoát liên quan đến một công %bcụ xác định định dạng. Giống như:printf %s%\ d%b thing 3 "${var+\cquit printing if set}\nelse do a newline" and 0 keep\ going.
mikeerv

Câu trả lời:


18

Bạn có ba vấn đề:

  1. Với read, nếu có ít tên biến hơn các trường trong đầu vào, var cuối cùng sẽ được liên kết với tất cả các trường còn lại trên dòng, với các dấu phân cách. Điều đó có nghĩa rằng $eđược 5 6trong ví dụ bất ngờ đầu tiên của bạn.
  2. Bởi vì tất cả $a.. $ekhông được trích dẫn, giá trị của chúng trải qua quá trình tách trường . Nếu $egiữ " 5 6" thì nó mở rộng thành hai đối số cho lệnh.
  3. printftiêu thụ tất cả các đối số của nó, sử dụng nhiều đối số cùng một lúc khi có %sự thay thế, lặp đi lặp lại. Điều này được chôn trong tài liệu như:

    Các formattoán hạng sẽ được tái sử dụng thường xuyên như cần thiết để đáp ứng các toán hạng đối số. Bất kỳ chỉ định bổ sung choặc schuyển đổi sẽ được đánh giá như thể một đối số chuỗi null được cung cấp; các thông số kỹ thuật chuyển đổi bổ sung khác sẽ được đánh giá như thể một đối số bằng không được cung cấp.

    Nói cách khác, nếu có các đối số không được sử dụng, nó sẽ bắt đầu lại và xử lý chúng ngay từ đầu, bao gồm toàn bộ chuỗi định dạng. Điều này hữu ích khi bạn muốn định dạng toàn bộ một mảng, nói:

    printf '%b ' "${array[@]}"

    printfLệnh của bạn nhận được một đối số từ mỗi $a.. $d, và sau đó nhiều đối số còn lại từ đó $e. Khi $elà " 5 6", printfcó hai đi xung quanh, lần thứ hai chỉ cần 6định dạng. Khi nó 5 6 7 8 9 10có đầy đủ các thay thế cho lần in thứ hai.


Bạn có thể tránh tất cả những điều này bằng cách thêm một trường giả thêm vào readvà trích dẫn các thay thế tham số của bạn (luôn luôn là một ý tưởng tốt):

read  a b c d e dummy
printf "> %s %s %s %s %s <" "$a" "$b" "$c" "$d" "$e"

Điều này sẽ cung cấp cho:

Enter 5 words : 
1 2 3 4 5 6 7 8 9 10
> 1 2 3 4 5 <

dummynhận được tất cả các trường bổ sung và printfchỉ nhận được năm đối số bạn mong đợi.


Câu hỏi chỉnh sửa thứ hai của bạn có một câu trả lời tương tự: chỉ anhận được một giá trị khi IFSkhông có khoảng trắng. Điều đó có nghĩa là $b.. $emở rộng thành không có gì, vì vậy printfchỉ nhận được một đối số duy nhất. Không gian của bạn từ chuỗi định dạng được in, không có gì được thay thế ở giữa chúng ("như thể một đối số chuỗi null được cung cấp").


Tôi lại kiểm tra tập lệnh thứ 2 bằng cách sử dụng "$ a" ..... "$ e". Kịch bản thứ hai đang đưa ra cùng một vấn đề một lần nữa.

3
Trích dẫn sẽ không tạo ra sự khác biệt cho kịch bản thứ hai. acó giá trị 1 2 3 4 5dưới dạng một chuỗi và nó được thay thế cùng một lúc.
Michael Homer

6
 printf "> %s < " 1 2 3

sẽ in

 > 1 <> 2 <> 3 <

  printf "> %s %s <" 1 2 3

in

 > 1 2 <> 3  <

printf ăn hết tất cả các đối số để thỏa mãn chuỗi định dạng của nó và sau đó lặp lại cho đến khi tất cả các đối số được xử lý.

Các tập lệnh thứ hai hoạt động vì chỉ $ađược gán cho và do đó lệnh không tràn vào các lần lặp bổ sung (chỉ có một lần lặp).


Hành vi này được ghi lại trong văn bản được cung cấp với help printf:

... Định dạng được sử dụng lại khi cần thiết để sử dụng tất cả các đối số. Nếu có ít đối số hơn định dạng yêu cầu, các đặc tả định dạng bổ sung sẽ hoạt động như thể một giá trị 0 hoặc chuỗi null, nếu phù hợp, đã được cung cấp. ...

và được ủy quyền bởi http://pub.opengroup.org/onlinepub/9699919799/utilities/printf.html


Tại sao lại có hành vi này? nó được ghi nhận?
Shiplu Mokaddim

1
@Shiplu đã thêm một đoạn về nơi ghi lại hành vi và tiêu chuẩn yêu cầu hành vi.
PSkocik
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.