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

123

Misalnya, diberikan:

USCAGoleta9311734.5021-120.1287855805

Saya ingin mengekstrak hanya:

US
Greg
sumber
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
related: unix.stackexchange.com/questions/3454/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

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.

paxdiablo
sumber
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.

ennuikiller
sumber
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 "/"
Dijeda sampai pemberitahuan lebih lanjut.
sumber
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…

Dominic Mitchell
sumber
ini jawaban termudah dan paling sederhana! bekerja seperti pesona
aloha
7

Grep saja:

echo 'abcdef' | grep -Po "^.."        # ab
Amir Mehler
sumber
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
bschlueter.dll
sumber
5

colrm - menghapus kolom dari file

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

cat file | colrm 3
Ian Yang
sumber
4

Terlambat memang tapi ini dia

sed 's/.//3g'

Atau

awk NF=1 FPAT=..

Atau

perl -pe '$_=unpack a2'
Steven Penny
sumber
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.

Juan
sumber
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;"
palswim
sumber
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]'
Matias Barrios
sumber
0
perl -ple 's/^(..).*/$1/'
dsm
sumber
Anda lupa menggemakan string ke dalamnya.
Chas. Owens
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

Jambobond
sumber
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

draegtun.dll
sumber
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