Saya mencoba untuk menetapkan variabel dalam skrip sh ke 3 karakter terakhir dari nama dasar file (dengan nama dasar yang saya maksud tanpa path dan tanpa akhiran). Saya telah berhasil melakukan ini, tetapi semata-mata karena penasaran, saya bertanya-tanya apakah ada perintah tunggal yang lebih pendek yang dapat saya gunakan. Awalnya saya punya satu kalimat awk
, tapi itu agak panjang. Saat ini saya memiliki skrip dua baris ini (dengan asumsi nama file lengkap ada di $1
):
filebase=`basename "$1"`
lastpart=`echo -n ${filebase%.*} | tail -c3`
Jadi misalnya, "/path/to/somefile.txt" berakhir dengan "ile" di $lastpart
.
Dapatkah saya entah bagaimana menggabungkan basename
dan sedikit untuk menghapus sufiks menjadi satu perintah, dan apakah ada cara untuk mengirimnya tail
(atau sesuatu yang bisa saya gunakan) tanpa menggunakan pipa? Akhiran tidak diketahui jadi saya tidak bisa mendasarkannya sebagai parameter basename
.
Tujuan utama sebenarnya tidak sesingkat mungkin, untuk dibaca sekilas mungkin. Konteks sebenarnya dari semua ini adalah pertanyaan tentang Superuser ini , di mana saya mencoba memberikan jawaban yang cukup sederhana.
sumber
file.one.two.three
? Apakah kamu mauile
atautwo
?two
akan bekerja; ekstensi itu akan.three
saya kira.Jawaban:
Itu pekerjaan yang khas untuk
expr
:Jika Anda tahu nama file Anda memiliki format yang diharapkan (berisi satu dan hanya satu titik dan setidaknya 3 karakter sebelum titik), itu dapat disederhanakan menjadi:
Perhatikan bahwa status keluar akan menjadi nol jika tidak ada kecocokan, tetapi juga jika bagian yang cocok adalah angka yang dipecahkan menjadi 0. (seperti untuk
a000.txt
ataua-00.txt
)Dengan
zsh
:(
:t
untuk ekor (nama belakang),:r
untuk istirahat (dengan ekstensi dihapus)).sumber
expr
adalah satu lagi yang perlu saya kenal. Saya sangat sukazsh
solusi secara umum (saya baru saja membaca tentang dukungannya untuk substitusi bersarang di sisi kiri${}
kemarin juga dan berharapsh
memiliki hal yang sama), itu hanya mengecewakan bahwa itu tidak selalu hadir secara default.expr
itu bukan POSIX. Tentu saja. Meskipun jarang built-in.Yang pertama menghapus tiga karakter terakhir dari
$var
kemudian menghapus dari$var
hasil penghapusan itu - yang mengembalikan tiga karakter terakhir$var
. Berikut adalah beberapa contoh yang lebih khusus ditujukan untuk menunjukkan bagaimana Anda dapat melakukan hal seperti itu:Anda tidak perlu menyebarkan semua ini melalui begitu banyak perintah. Anda dapat memadatkan ini:
Menggabungkan
$IFS
denganset
parameter shell juga bisa menjadi cara yang sangat efektif untuk penguraian dan pengeboran melalui variabel shell:Yang akan membuat Anda hanya tiga karakter segera sebelum periode pertama setelah yang terakhir
/
di$path
. Jika Anda ingin mengambil hanya tiga karakter pertama segera sebelum yang terakhir.
dalam$path
(misalnya, jika ada kemungkinan lebih dari satu.
dalam nama file) :Dalam kedua kasus yang dapat Anda lakukan:
Dan...
... akan mencetak yang mengikuti
.
Jika Anda tidak keberatan menggunakan program eksternal yang dapat Anda lakukan:
Jika ada kemungkinan
\n
karakter ewline dalam nama file (tidak berlaku untuk solusi shell asli - mereka semua mengatasinya) :sumber
$base
sana, yang terbaik yang bisa saya lakukan adalah tiga barisname=${var##*/} ; base=${name%%.*} ; lastpart=${base#${base%???}}
. Di sisi positifnya itu adalah bash murni, tetapi masih 3 baris. (Dalam contoh Anda dari "/tmp/file.txt" Saya perlu "ile" daripada "file".) Saya baru saja belajar banyak tentang penggantian parameter; Saya tidak tahu itu bisa melakukan itu ... sangat berguna. Saya merasa itu sangat mudah dibaca, juga, secara pribadi.%
alih-alih%%
menghapus sufiks, dan saya sebenarnya tidak perlu menghapus jalur, jadi saya bisa mendapatkan dua baris yang lebih bagusnoextn=${var%.*} ; lastpart=${noextn#${noextn%???}}
.$IFS
di${noextn}
dan Anda tidak mengutip ekspansi. Jadi, ini lebih aman:lastpart=${noextn#"${noextn%???}"}
Jika Anda dapat menggunakan
perl
:sumber
perl -e 'shift =~ /(.{3})\.[^.]*$/ && print $1' $filename
. Tambahanbasename
akan diperlukan jika nama file mungkin tidak mengandung akhiran tetapi beberapa direktori di jalur tidak.perl -e 'shift =~ m#(.{3})(?:\.[^./]*)?$# && print $1' $filename
sed
bekerja untuk ini:Atau
Jika Anda
sed
tidak mendukung-r
, ganti saja instance()
dengan\(
dan\)
, dan kemudian-r
tidak diperlukan.sumber
Jika perl tersedia, saya merasa ini bisa lebih mudah dibaca daripada solusi lain, khususnya karena bahasa
/x
regexnya lebih ekspresif dan memiliki modifikator, yang memungkinkan untuk menulis regex yang lebih jelas:Ini tidak mencetak apa pun jika tidak ada yang cocok (jika nama dasarnya tidak memiliki ekstensi atau jika root sebelum ekstensi terlalu pendek). Tergantung pada kebutuhan Anda, Anda dapat menyesuaikan regex. Regex ini memberlakukan batasan:
Menggunakan ini dalam substitusi perintah memiliki masalah normal dengan menghapus terlalu banyak baris baru, masalah yang juga mempengaruhi jawaban Stéphane. Ini dapat ditangani dalam kedua kasus, tetapi sedikit lebih mudah di sini:
sumber
Python2.7
sumber
Saya pikir fungsi bash ini, pathStr (), akan melakukan apa yang Anda cari.
Itu tidak membutuhkan awk, sed, grep, perl atau expr. Hanya menggunakan bash builtins sehingga cukup cepat.
Saya juga menyertakan fungsi argsNumber dan isOption yang tergantung, tetapi fungsinya dapat dengan mudah dimasukkan ke pathStr.
Fungsi dependen ifHelpShow tidak termasuk karena memiliki banyak subdependensi untuk menghasilkan teks bantuan baik pada commandline terminal atau ke kotak dialog GUI via YAD . Teks bantuan yang diteruskan disertakan untuk dokumentasi. Anjurkan jika Anda ingin ifHelpShow dan tanggungannya.
SUMBER DAYA
sumber
bash
isme - tampaknya lebih sederhana dari ini. Apa itu${#@}
?$#
adalah sintaks untuk jumlah argumen, dan juga digunakan di tempat lain di sini.${#@}
dalam banyak kasus tidak setara - spec POSIX menyatakan hasil dari setiap ekspansi parameter pada salah satu$@
atau$*
tidak ditentukan, sayangnya. Mungkin berfungsi,bash
tetapi itu bukan fitur yang dapat diandalkan, saya kira itulah yang ingin saya katakan.,