Bagaimana cara mengekstrak dua karakter pertama dari sebuah string dalam skrip shell?


123

Misalnya, diberikan:

USCAGoleta9311734.5021-120.1287855805

Saya ingin mengekstrak hanya:

US

6
Terimakasih semuanya. Saya akhirnya menggunakan 'cut -c1-2', sejujurnya saya bahkan tidak tahu 'cut' ada di sana. Saya ingin mengatakan bahwa saya cukup berpengalaman dalam baris perintah - tetapi tampaknya saya harus banyak belajar.
Greg

1
@Greg, ketahuilah bahwa cut dijalankan sebagai proses terpisah - ini akan lebih lambat daripada solusi internal-bash yang saya posting di sampingnya dalam jawaban saya. Itu tidak akan membuat perbedaan apa pun kecuali Anda memproses kumpulan data yang sangat besar tetapi Anda perlu mengingatnya.
paxdiablo

Sunting Sebenarnya, menurut saya baris kode ini mungkin akan dieksekusi sekitar 50.000 kali per laporan. Jadi saya mungkin hanya menggunakan metode Bash internal - yang seperti yang Anda katakan akan menghemat beberapa sumber daya yang sangat dibutuhkan.
Greg

Jawaban:


180

Mungkin metode yang paling efisien, jika Anda menggunakan bashshell (dan Anda tampaknya, berdasarkan komentar Anda), adalah menggunakan varian sub-string dari perluasan parameter:

pax> long="USCAGol.blah.blah.blah"
pax> short="${long:0:2}" ; echo "${short}"
US

Ini akan ditetapkan shortmenjadi dua karakter pertama dari long. Jika longlebih pendek dari dua karakter, shortakan identik dengannya.

Metode dalam shell ini biasanya lebih baik jika Anda akan melakukannya banyak (seperti 50.000 kali per laporan seperti yang Anda sebutkan) karena tidak ada overhead pembuatan proses. Semua solusi yang menggunakan program eksternal akan mengalami overhead tersebut.

Jika Anda juga ingin memastikan panjang minimum , Anda dapat menambahkannya terlebih dahulu dengan sesuatu seperti:

pax> long="A"
pax> tmpstr="${long}.."
pax> short="${tmpstr:0:2}" ; echo "${short}"
A.

Ini akan memastikan bahwa apa pun yang panjangnya kurang dari dua karakter dilapisi di sebelah kanan dengan titik (atau yang lainnya, hanya dengan mengubah karakter yang digunakan saat membuat tmpstr). Tidak jelas apakah Anda membutuhkan ini, tetapi saya pikir saya akan memasukkannya untuk kelengkapan.


Karena itu, ada sejumlah cara untuk melakukan ini dengan program eksternal (seperti jika Anda tidak memiliki bashtersedia untuk Anda), beberapa di antaranya adalah:

short=$(echo "${long}" | cut -c1-2)
short=$(echo "${long}" | head -c2)
short=$(echo "${long}" | awk '{print substr ($0, 0, 2)}'
short=$(echo "${long}" | sed 's/^\(..\).*/\1/')

Dua pertama (cut dan head) identik untuk string satu baris - pada dasarnya keduanya hanya mengembalikan dua karakter pertama. Mereka berbeda dalam hal itu cutakan memberi Anda dua karakter pertama dari setiap baris danhead akan memberi Anda dua karakter pertama dari keseluruhan masukan

Yang ketiga menggunakan fungsi awksub-string untuk mengekstrak dua karakter pertama dan yang keempat menggunakan sedgrup tangkap (menggunakan() dan \1) untuk menangkap dua karakter pertama dan mengganti seluruh baris dengan mereka. Keduanya mirip cut- keduanya memberikan dua karakter pertama dari setiap baris dalam masukan.

Tidak ada yang penting jika Anda yakin masukan Anda adalah satu baris, semuanya memiliki efek yang sama.


Saya lebih suka menggunakan printf '%s'daripada echojika ada karakter aneh dalam string: stackoverflow.com/a/40423558/895245 Untuk POSIX yang terobsesi: head -cbukan POSIX, cut -cdan awk substr, sed \1tidak yakin.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@CiroSantilli 新疆 改造 中心 996ICU 六四 事件 menggunakan printf, Anda bahkan tidak memerlukan program tambahan. Lihat jawaban saya .
bschlueter

60

cara termudah adalah

${string:position:length}

Di mana ini mengekstrak $lengthsubstring dari $stringpada $position.

Ini adalah bash bawaan sehingga awk atau sed tidak diperlukan.


Ini adalah cara singkat, manis, dan termudah untuk mendapatkan substring.
ani627

34

Anda mendapatkan beberapa jawaban bagus dan saya akan memilih Bash bawaan sendiri, tetapi karena Anda bertanya tentang seddan awkdan ( hampir ) tidak ada orang lain yang menawarkan solusi berdasarkan jawaban tersebut , saya menawarkan ini kepada Anda:

echo "USCAGoleta9311734.5021-120.1287855805" | awk '{print substr($0,0,2)}'

dan

echo "USCAGoleta9311734.5021-120.1287855805" | sed 's/\(^..\).*/\1/'

Yang awksatu seharusnya cukup jelas, tapi berikut penjelasannya sed:

  • gantikan "s /"
  • grup "()" dari dua karakter apa pun ".." dimulai dari awal baris "^" dan diikuti oleh karakter apa pun "." diulangi nol kali atau lebih "*" (garis miring terbalik diperlukan untuk menyela beberapa karakter khusus)
  • oleh "/" isi dari grup pertama (dan hanya, dalam kasus ini) (di sini garis miring terbalik adalah pelolosan khusus yang mengacu pada sub-ekspresi yang cocok)
  • selesai "/"

1
Dalam string awk mulai dari indeks 1, jadi Anda harus menggunakan substr($0,1,2).
Isaac

8

Jika Anda ikut bash, Anda dapat mengatakan:

bash-3.2$ var=abcd
bash-3.2$ echo ${var:0:2}
ab

Ini mungkin yang Anda butuhkan…


ini jawaban termudah dan paling sederhana! bekerja seperti pesona
aloha

7

Grep saja:

echo 'abcdef' | grep -Po "^.."        # ab

Sesuai dengan kebutuhan saya. Anda dapat menghapus -Popsi untuk membuatnya lebih pendek. Semua regex akan memahami pola itu.
datashaman

6

Anda dapat menggunakan printf:

$ original='USCAGoleta9311734.5021-120.1287855805'
$ printf '%-.2s' "$original"
US

5

colrm - menghapus kolom dari file

Untuk meninggalkan dua karakter pertama, hapus saja kolom mulai dari 3

cat file | colrm 3

4

Terlambat memang tapi ini dia

sed 's/.//3g'

Atau

awk NF=1 FPAT=..

Atau

perl -pe '$_=unpack a2'

2

Jika Anda ingin menggunakan skrip shell dan tidak bergantung pada ekstensi non-posix (seperti yang disebut bashisme), Anda dapat menggunakan teknik yang tidak memerlukan alat eksternal forking seperti grep, sed, cut, awk, dll., Yang kemudian buat skrip Anda kurang efisien. Mungkin efisiensi dan portabilitas posix tidak penting dalam kasus penggunaan Anda. Tetapi jika itu (atau hanya sebagai kebiasaan baik), Anda dapat menggunakan metode opsi perluasan parameter berikut untuk mengekstrak dua karakter pertama dari variabel shell:

$ sh -c 'var=abcde; echo "${var%${var#??}}"'
ab

Ini menggunakan perluasan parameter "awalan terkecil" untuk menghapus dua karakter pertama (ini adalah ${var#??}bagian), kemudian perluasan parameter "akhiran terkecil" (${var% bagian) untuk menghapus string semua-kecuali-dua-karakter-pertama dari aslinya nilai.

Metode ini sebelumnya dijelaskan dalam jawaban ini untuk pertanyaan "Shell = Periksa apakah variabel dimulai dengan #". Jawaban itu juga menjelaskan beberapa metode perluasan parameter serupa yang dapat digunakan dalam konteks yang sedikit berbeda dengan metode yang diterapkan pada pertanyaan awal di sini.


Jawaban terbaik, harus di atas. tidak ada garpu, tidak ada bashisme. bekerja bahkan dengan cangkang kecil seperti dasbor.
Exore

1

Jika sistem Anda menggunakan shell yang berbeda (bukan bash), tetapi sistem Anda memilikinya bash, maka Anda masih dapat menggunakan manipulasi string inheren bashdengan memanggil bashdengan variabel:

strEcho='echo ${str:0:2}' # '${str:2}' if you want to skip the first two characters and keep the rest
bash -c "str=\"$strFull\";$strEcho;"

Ini menggunakan metode yang sama dengan jawaban utama , hanya memanggil bashjika Anda belum menggunakannya.
palswim

Sayangnya, ini datang dengan semua overhead untuk meminta proses lain, tetapi kadang-kadang overhead itu tidak menjadi masalah sebanyak kesederhanaan dan keakraban.
palswim

1

Hanya untuk kesenangan saya akan menambahkan beberapa, meskipun mereka terlalu rumit dan tidak berguna, mereka tidak disebutkan:

head -c 2 <( echo 'USCAGoleta9311734.5021-120.1287855805')

echo 'USCAGoleta9311734.5021-120.1287855805' | dd bs=2 count=1 status=none

sed -e 's/^\(.\{2\}\).*/\1/;' <( echo 'USCAGoleta9311734.5021-120.1287855805')

cut -c 1-2 <( echo 'USCAGoleta9311734.5021-120.1287855805')

python -c "print(r'USCAGoleta9311734.5021-120.1287855805'[0:2])"

ruby -e 'puts "USCAGoleta9311734.5021-120.1287855805"[0..1]'


0

jika mystring = USCAGoleta9311734.5021-120.1287855805

print substr(mystring,0,2)

akan mencetak AS

dimana 0 adalah posisi awal dan 2 adalah bagaimana karakter untuk dibaca


Katakan ... bukankah itu GW-BASIC? Oh, tunggu, itu awk. Maaf, awalnya saya tidak tahu.
Dijeda sampai pemberitahuan lebih lanjut.

0

Apakah ini yang Anda kejar?

my $string = 'USCAGoleta9311734.5021-120.1287855805';

my $first_two_chars = substr $string, 0, 2;

ref: substr


1
mengingat bahwa dia cenderung memanggil ini dari cangkang, bentuk yang lebih baik adalahperl -e 'print substr $ARGV[0], 0, 2' 'USCAGoleta9311734.5021-120.1287855805'
Chas. Owens
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.