trừu tượng
In các dòng không có dòng mới, chỉ thêm một dòng mới nếu có một dòng khác để in.
$ printf 'one\ntwo\n' |
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
Các giải pháp khác
Nếu chúng tôi đang làm việc với một tệp, chúng tôi có thể cắt bớt một ký tự từ nó (nếu nó kết thúc trên một dòng mới):
removeTrailNewline () {[[$ (đuôi -c 1 "$ 1")]] || cắt ngắn -s-1 "$ 1"; }
Đó là một giải pháp nhanh vì nó chỉ cần đọc một ký tự từ tệp và sau đó xóa trực tiếp ( truncate
) mà không cần đọc toàn bộ tệp.
Tuy nhiên, trong khi làm việc với dữ liệu từ stdin (một luồng), dữ liệu phải được đọc, tất cả dữ liệu đó. Và, nó được "tiêu thụ" ngay khi đọc. Không có backtrack (như với cắt ngắn). Để tìm điểm cuối của luồng chúng ta cần đọc đến cuối luồng. Tại thời điểm đó, không có cách nào để quay trở lại luồng đầu vào, dữ liệu đã được "tiêu thụ". Điều này có nghĩa là dữ liệu phải được lưu trữ trong một số dạng bộ đệm cho đến khi chúng ta khớp với cuối luồng và sau đó làm một cái gì đó với dữ liệu trong bộ đệm.
Rõ ràng nhất của các giải pháp là chuyển đổi luồng thành một tệp và xử lý tệp đó. Nhưng câu hỏi yêu cầu một số loại bộ lọc của luồng. Không phải về việc sử dụng các tập tin bổ sung.
Biến đổi
Giải pháp ngây thơ sẽ là bắt toàn bộ đầu vào thành một biến:
FilterOne(){ filecontents=$(cat; echo "x"); # capture the whole input
filecontents=${filecontents%x}; # Remove the "x" added above.
nl=$'\n'; # use a variable for newline.
printf '%s' "${filecontents%"$nl"}"; # Remove newline (if it exists).
}
printf 'one\ntwo' | FilterOne ; echo 1done
printf 'one\ntwo\n' | FilterOne ; echo 2done
printf 'one\ntwo\n\n' | FilterOne ; echo 3done
ký ức
Có thể tải toàn bộ tập tin trong bộ nhớ bằng sed. Trong sed không thể tránh được dòng mới trên dòng cuối cùng. GNU sed có thể tránh in một dòng mới, nhưng chỉ khi tệp nguồn đã bị thiếu. Vì vậy, không, sed đơn giản không thể giúp đỡ.
Ngoại trừ trên GNU awk với -z
tùy chọn:
sed -z 's/\(.*\)\n$/\1/'
Với awk (bất kỳ awk), hãy nhét toàn bộ luồng và printf
nó không có dòng mới.
awk ' { content = content $0 RS }
END { gsub( "\n$", "", content ); printf( "%s", content ) }
'
Tải toàn bộ tập tin vào bộ nhớ có thể không phải là một ý tưởng hay, nó có thể tiêu tốn rất nhiều bộ nhớ.
Hai dòng trong bộ nhớ
Trong awk, chúng ta có thể xử lý hai dòng trên mỗi vòng lặp bằng cách lưu trữ dòng trước đó trong một biến và in dòng hiện tại:
awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'
Xử lý trực tiếp
Nhưng chúng ta có thể làm tốt hơn.
Nếu chúng tôi in dòng hiện tại mà không có dòng mới và chỉ in dòng mới khi dòng tiếp theo tồn tại, chúng tôi sẽ xử lý một dòng tại một dòng và dòng cuối cùng sẽ không có dòng mới:
awk 'NR == 1 {printf ("% s", $ 0); tiếp theo}; {printf ("\ n% s", $ 0)} '
Hoặc, được viết theo một cách khác:
awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'
Hoặc là:
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'
Vì thế:
$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
chomp
, vìchomp
chỉ xóa tối đa một dòng mới.