Trong những trường hợp thường xuyên đáng ngạc nhiên khi những gì bạn thực sự cần làm là xử lý tất cả các dòng không trống bên trong một biến số (bao gồm cả đếm chúng), bạn có thể đặt IFS thành một dòng mới và sau đó sử dụng cơ chế tách từ của shell để phá vỡ Các dòng không trống cách nhau.
Ví dụ: đây là một hàm shell nhỏ có tổng các dòng không trống bên trong tất cả các đối số được cung cấp:
lines() (
IFS='
'
set -f #disable pathname expansion
set -- $*
echo $#
)
Dấu ngoặc đơn, thay vì dấu ngoặc nhọn, được sử dụng ở đây để tạo thành lệnh ghép cho thân hàm. Điều này làm cho hàm thực thi trong một lớp con để nó không gây ô nhiễm cho cài đặt mở rộng biến IFS và tên đường dẫn bên ngoài trên mỗi cuộc gọi.
Nếu bạn muốn lặp lại các dòng không trống, bạn có thể thực hiện tương tự:
IFS='
'
set -f
for line in $lines
do
printf '[%s]\n' $line
done
Thao tác IFS theo cách này là một kỹ thuật thường bị bỏ qua, cũng thuận tiện để thực hiện những việc như phân tích tên đường dẫn có thể chứa khoảng trắng từ đầu vào cột được phân tách bằng tab. Tuy nhiên, bạn cần lưu ý rằng việc cố tình xóa ký tự khoảng trắng thường có trong cài đặt mặc định của tab không gian-tab-dòng mới có thể vô hiệu hóa việc tách từ ở những nơi bạn thường thấy.
Ví dụ: nếu bạn đang sử dụng các biến để xây dựng một dòng lệnh phức tạp cho một cái gì đó như ffmpeg
, bạn có thể chỉ muốn bao gồm -vf scale=$scale
khi biến scale
được đặt thành một thứ gì đó không trống. Thông thường bạn có thể đạt được điều này với ${scale:+-vf scale=$scale}
nhưng nếu IFS không bao gồm ký tự không gian thông thường của nó tại thời điểm mở rộng tham số này, khoảng trống giữa -vf
và scale=
sẽ không được sử dụng làm dấu tách từ và ffmpeg
sẽ được chuyển qua -vf scale=$scale
như một đối số duy nhất, Điều đó sẽ không hiểu.
Để khắc phục điều đó, bạn cần phải đảm bảo IFS được đặt bình thường hơn trước khi thực hiện ${scale}
mở rộng hoặc thực hiện hai lần mở rộng : ${scale:+-vf} ${scale:+scale=$scale}
. Việc tách từ mà shell thực hiện trong quá trình phân tích cú pháp các dòng lệnh ban đầu, trái với việc phân tách nó thực hiện trong giai đoạn mở rộng xử lý các dòng lệnh đó, không phụ thuộc vào IFS.
Một cái gì đó khác có thể có giá trị trong khi bạn thực hiện loại điều này sẽ tạo ra hai biến vỏ toàn cầu để chỉ giữ một tab và chỉ là một dòng mới:
t=' '
n='
'
Bằng cách đó, bạn chỉ có thể bao gồm $t
và $n
mở rộng nơi bạn cần các tab và dòng mới, thay vì xả rác tất cả mã của bạn với khoảng trắng được trích dẫn. Nếu bạn muốn tránh hoàn toàn khoảng trắng được trích dẫn trong vỏ POSIX không có cơ chế nào khác để làm như vậy, printf
có thể giúp bạn mặc dù bạn cần một chút lo lắng để giải quyết việc xóa các dòng mới trong phần mở rộng lệnh:
nt=$(printf '\n\t')
n=${nt%?}
t=${nt#?}
Đôi khi, thiết lập IFS như thể nó là một biến môi trường cho mỗi lệnh hoạt động tốt. Ví dụ: đây là một vòng lặp đọc tên đường dẫn được phép chứa khoảng trắng và hệ số tỷ lệ từ mỗi dòng của tệp đầu vào được phân định bằng tab:
while IFS=$t read -r path scale
do
ffmpeg -i "$path" ${scale:+-vf scale=$scale} "${path%.*}.out.mkv"
done <recode-queue.txt
Trong trường hợp này, phần read
dựng sẵn thấy IFS được đặt thành một tab, do đó, nó sẽ không phân chia dòng đầu vào mà nó đọc trên các khoảng trắng. Nhưng IFS=$t set -- $lines
không hoạt động: shell mở rộng $lines
khi nó xây dựng các set
đối số của buildin trước khi thực hiện lệnh, do đó, việc thiết lập IFS tạm thời theo cách chỉ áp dụng trong quá trình thực thi chính nội dung đã quá muộn. Đây là lý do tại sao các đoạn mã tôi đã đưa ra trên tất cả các IFS đặt ở một bước riêng biệt và tại sao chúng phải giải quyết vấn đề bảo tồn nó.
wc -l
hoàn toàn tương đương với bản gốc:<<<$foo
thêm một dòng mới vào giá trị của$foo
(ngay cả khi$foo
trống). Tôi giải thích trong câu trả lời của mình tại sao điều này có thể không phải là điều muốn, nhưng đó là điều được hỏi.