sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Tôi hy vọng sed
tập lệnh này sẽ chèn tab
vào trước mỗi dòng, $filename
tuy nhiên nó không phải vậy. Vì lý do nào đó, nó đang chèn một t
thay thế.
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Tôi hy vọng sed
tập lệnh này sẽ chèn tab
vào trước mỗi dòng, $filename
tuy nhiên nó không phải vậy. Vì lý do nào đó, nó đang chèn một t
thay thế.
Câu trả lời:
Không phải tất cả các phiên bản đều sed
hiểu \t
. Thay vào đó, chỉ cần chèn một tab chữ (nhấn Ctrl- Vsau đó Tab).
\t
ở phần thay thế của biểu thức (nó được công nhận \t
ở phần khớp mẫu tốt)
Sử dụng Bash, bạn có thể chèn ký tự TAB theo chương trình như sau:
TAB=$'\t'
echo 'line' | sed "s/.*/${TAB}&/g"
echo 'line' | sed 's/.*/'"${TAB}"'&/g' # use of Bash string concatenation
$'string'
sự giải thích nhưng thiếu. Trong thực tế, tôi nghi ngờ, vì cách sử dụng cực kỳ khó hiểu mà bạn có thể hiểu chưa đầy đủ (như hầu hết chúng ta làm với bash). Xem giải thích của tôi bên dưới: stackoverflow.com/a/43190120/117471
$TAB
bên trong dấu ngoặc kép, vì vậy bạn sẽ cần sử dụng dấu ngoặc kép.
*
bên trong dấu ngoặc kép ... điều này sẽ được coi là một hình cầu, không phải là regex mà bạn dự định.
@sedit đã đi đúng hướng, nhưng hơi khó xử khi xác định một biến.
Cách để làm điều này trong bash là đặt một ký hiệu đô la trước chuỗi được trích dẫn duy nhất của bạn.
$ echo -e '1\n2\n3'
1
2
3
$ echo -e '1\n2\n3' | sed 's/.*/\t&/g'
t1
t2
t3
$ echo -e '1\n2\n3' | sed $'s/.*/\t&/g'
1
2
3
Nếu chuỗi của bạn cần bao gồm mở rộng biến, bạn có thể đặt các chuỗi được trích dẫn lại với nhau như sau:
$ timestamp=$(date +%s)
$ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g'
1491237958 1
1491237958 2
1491237958 3
Trong bash $'string'
gây ra "mở rộng ANSI-C". Và đó là những gì hầu hết chúng ta mong đợi khi chúng tôi sử dụng những thứ như \t
, \r
, \n
, vv Từ: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting
Các từ có dạng $ 'string' được xử lý đặc biệt. Từ mở rộng thành chuỗi , với các ký tự thoát ra sau dấu gạch chéo ngược được thay thế như được chỉ định bởi tiêu chuẩn ANSI C. Các chuỗi thoát dấu gạch chéo ngược, nếu có, được giải mã ...
Kết quả mở rộng được trích dẫn một lần, như thể ký hiệu đô la không có mặt.
Cá nhân tôi nghĩ rằng hầu hết các nỗ lực để tránh bash là ngớ ngẩn bởi vì tránh bash này KHÔNG * làm cho mã của bạn trở nên linh hoạt. (Mã của bạn sẽ kém giòn hơn nếu bạn tập hợp nó bash -eu
hơn là nếu bạn cố gắng tránh bash và sử dụng sh
[trừ khi bạn là một ninja POSIX tuyệt đối].) * câu trả lời.
$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
1
2
3
* Câu trả lời tốt nhất? Có, bởi vì một ví dụ về những gì mà hầu hết các trình viết lệnh shell chống bash sẽ làm sai trong mã của họ là sử dụng echo '\t'
như trong câu trả lời của @ robrecord . Điều đó sẽ hoạt động đối với tiếng vang GNU, nhưng không hiệu quả với tiếng vang BSD. Điều đó được giải thích bởi The Open Group tại http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 Và đây là một ví dụ về lý do tại sao việc cố gắng tránh các cơ sở sản xuất kinh doanh thường không thành công.
Tôi đã sử dụng một cái gì đó như thế này với Bash shell trên Ubuntu 12.04 (LTS):
Để nối một dòng mới với tab, thứ hai khi đối sánh đầu tiên :
sed -i '/first/a \\t second' filename
Để thay thế đầu tiên bằng tab, thứ hai :
sed -i 's/first/\\t second/g' filename
\\t
và không \t
.
Sử dụng $(echo '\t')
. Bạn sẽ cần trích dẫn xung quanh mẫu.
Ví dụ. Để xóa một tab:
sed "s/$(echo '\t')//"
echo '\t'
sẽ xuất ra 2 ký tự riêng biệt. Cách di động POSIX là sử dụng printf '\t'
. Đây là lý do tại sao tôi nói: Đừng cố gắng làm cho mã của bạn trở nên di động bằng cách không sử dụng bash. Nó khó hơn bạn nghĩ. Sử dụng bash
là điều dễ di chuyển nhất mà hầu hết chúng ta có thể làm.
Bạn không cần phải sử dụng sed
để thay thế khi trên thực tế, bạn chỉ muốn chèn một tab vào trước dòng. Thay thế cho trường hợp này là một hoạt động tốn kém so với chỉ in nó ra, đặc biệt là khi bạn đang làm việc với các tệp lớn. Nó cũng dễ đọc hơn vì nó không phải là regex.
ví dụ như sử dụng awk
awk '{print "\t"$0}' $filename > temp && mv temp $filename
Tôi đã sử dụng cái này trên Mac:
sed -i '' $'$i\\\n\\\thello\n' filename
sed
không hỗ trợ \t
, cũng không phải các chuỗi thoát khác như \n
cho vấn đề đó. Cách duy nhất tôi tìm thấy để làm điều đó là thực sự chèn ký tự tab trong tập lệnh bằng cách sử dụngsed
.
Điều đó nói rằng, bạn có thể muốn xem xét sử dụng Perl hoặc Python. Đây là một tập lệnh Python ngắn mà tôi đã viết mà tôi sử dụng cho tất cả các luồng regex'ing:
#!/usr/bin/env python
import sys
import re
def main(args):
if len(args) < 2:
print >> sys.stderr, 'Usage: <search-pattern> <replace-expr>'
raise SystemExit
p = re.compile(args[0], re.MULTILINE | re.DOTALL)
s = sys.stdin.read()
print p.sub(args[1], s),
if __name__ == '__main__':
main(sys.argv[1:])
Thay vì BSD sed, tôi sử dụng perl:
ct@MBA45:~$ python -c "print('\t\t\thi')" |perl -0777pe "s/\t/ /g"
hi
Tôi nghĩ rằng những người khác đã làm sáng tỏ này đầy đủ cho cách tiếp cận khác ( sed
, AWK
, vv). Tuy nhiên, bash
các câu trả lời cụ thể của tôi (được thử nghiệm trên macOS High Sierra và CentOS 6/7) theo sau.
1) Nếu OP muốn sử dụng phương pháp tìm kiếm và thay thế tương tự như những gì họ đề xuất ban đầu, thì tôi sẽ đề xuất sử dụng perl
cho điều này, như sau. Lưu ý: dấu gạch chéo ngược trước dấu ngoặc đơn cho regex không cần thiết và dòng mã này phản ánh cách $1
sử dụng tốt hơn so \1
với perl
toán tử thay thế (ví dụ: theo tài liệu Perl 5 ).
perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename
2) Tuy nhiên, như đã chỉ ra bởi ghostdog74 , vì thao tác mong muốn thực sự là chỉ cần thêm một tab ở đầu mỗi dòng trước khi thay đổi tệp tmp thành tệp đầu vào / đích ( $filename
), tôi khuyên bạn nên sử dụng perl
lại nhưng với sửa đổi sau (S):
perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
3) Tất nhiên, tệp tmp là không cần thiết , vì vậy tốt hơn là chỉ làm mọi thứ 'tại chỗ' (thêm -i
cờ) và đơn giản hóa mọi thứ thành một lớp lót thanh lịch hơn với
perl -i -pe $'s/^/\t/' $filename