Bagaimana saya bisa mengonversi tab menjadi spasi di setiap file direktori (mungkin secara rekursif)?
Juga, apakah ada cara mengatur jumlah spasi per tab?
pr
adalah utilitas luar biasa untuk ini. Lihat jawaban ini .
Bagaimana saya bisa mengonversi tab menjadi spasi di setiap file direktori (mungkin secara rekursif)?
Juga, apakah ada cara mengatur jumlah spasi per tab?
pr
adalah utilitas luar biasa untuk ini. Lihat jawaban ini .
Jawaban:
Peringatan: Ini akan merusak repo Anda.
Ini akan merusak file biner , termasuk di bawah mereka
svn
,.git
! Baca komentar sebelum menggunakan!
find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +
File asli disimpan sebagai [filename].orig
.
Ganti '* .java' dengan akhiran file dari jenis file yang Anda cari. Dengan cara ini Anda dapat mencegah kerusakan file biner secara tidak sengaja.
Kerugian:
expand
.
find ./ -type f -exec sed -i 's/^\t/####/g' {} \;
. Tapi saya tidak menyadari perintah memperluas - sangat berguna!
Penggantian sederhana dengan sed
tidak apa-apa tapi bukan solusi terbaik. Jika ada ruang "ekstra" di antara tab, mereka akan tetap ada setelah penggantian, sehingga margin akan menjadi acak-acakan. Tab yang diperluas di tengah garis juga tidak akan berfungsi dengan benar. Di bash
, kita bisa mengatakan sebaliknya
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
untuk diterapkan expand
ke setiap file Java di pohon direktori saat ini. Hapus / ganti -name
argumen jika Anda menargetkan beberapa jenis file lainnya. Seperti yang disebutkan dalam salah satu komentar, berhati-hatilah saat mengeluarkan -name
atau menggunakan kartu pengganti yang lemah. Anda dapat dengan mudah menerima repositori dan file tersembunyi lainnya tanpa sengaja. Inilah sebabnya mengapa jawaban asli termasuk ini:
Anda harus selalu membuat salinan cadangan pohon sebelum mencoba sesuatu seperti ini jika terjadi kesalahan.
{}
. Sepertinya dia tidak tahu $0
kapan -c
digunakan. Kemudian dimo414 berubah dari penggunaan temp saya di direktori konversi menjadi /tmp
, yang akan jauh lebih lambat jika /tmp
berada pada titik mount yang berbeda. Sayangnya saya tidak memiliki kotak Linux yang tersedia untuk menguji $0
proposal Anda . Tapi saya pikir Anda benar.
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
sponge
dari joeyh.name/code/moreutils , Anda dapat menulisfind . -name '*.py' ! -type d -exec bash -c 'expand -t 8 "$0" | sponge "$0"' {} \;
find . -name '*'
, saya baru saja menghancurkan git repo lokal saya
Coba alat baris perintah expand
.
expand -i -t 4 input | sponge output
dimana
-i
digunakan untuk memperluas hanya tab utama di setiap baris;-t 4
berarti bahwa setiap tab akan dikonversi menjadi 4 spasi spasi (8 secara default).sponge
berasal dari moreutils
paket, dan menghindari membersihkan file input .Akhirnya, Anda dapat menggunakan gexpand
OSX, setelah menginstal coreutils
dengan Homebrew ( brew install coreutils
).
-i
ke expand
hanya mengganti tab terkemuka di setiap baris. Ini membantu menghindari penggantian tab yang mungkin menjadi bagian dari kode.
input
adalah file yang sama output
dengan bash clobbers konten bahkan sebelum memulai expand
. Beginilah cara >
kerjanya.
Mengumpulkan komentar terbaik dari jawaban Gene , solusi terbaik sejauh ini, adalah dengan menggunakan sponge
dari moreutils .
sudo apt-get install moreutils
# The complete one-liner:
find ./ -iname '*.java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;
Penjelasan:
./
secara rekursif mencari dari direktori saat ini-iname
adalah pencocokan huruf besar-kecil (untuk keduanya *.java
dan *.JAVA
suka)type -f
hanya menemukan file biasa (tidak ada direktori, binari atau symlink)-exec bash -c
menjalankan perintah berikut dalam subkulit untuk setiap nama file, {}
expand -t 4
perluas semua TAB menjadi 4 ruangsponge
menyerap input standar (dari expand
) dan menulis ke file (yang sama) *.CATATAN : * Pengalihan file sederhana ( > "$0"
) tidak akan berfungsi di sini karena akan menimpa file terlalu cepat .
Keuntungan : Semua izin file asli dipertahankan dan tidak ada tmp
file perantara yang digunakan.
Gunakan garis miring terbalik sed
.
Di linux:
Ganti semua tab dengan 1 tanda hubung di tempat, di semua file * .txt:
sed -i $'s/\t/-/g' *.txt
Ganti semua tab dengan 1 spasi di tempat, di semua file * .txt:
sed -i $'s/\t/ /g' *.txt
Ganti semua tab dengan 4 spasi di tempat, di semua file * .txt:
sed -i $'s/\t/ /g' *.txt
Di mac:
Ganti semua tab dengan 4 spasi di tempat, di semua file * .txt:
sed -i '' $'s/\t/ /g' *.txt
sed -i '' $'s/\t/ /g' $(find . -name "*.txt")
Anda dapat menggunakan pr
perintah yang tersedia secara umum (halaman manual di sini ). Misalnya, untuk mengonversi tab menjadi empat spasi, lakukan ini:
pr -t -e=4 file > file.expanded
-t
menekan tajuk-e=num
perluas tab ke num
spasiUntuk mengonversi semua file di pohon direktori secara rekursif, sambil melewatkan file biner:
#!/bin/bash
num=4
shopt -s globstar nullglob
for f in **/*; do
[[ -f "$f" ]] || continue # skip if not a regular file
! grep -qI "$f" && continue # skip binary files
pr -t -e=$num "$f" > "$f.expanded.$$" && mv "$f.expanded.$$" "$f"
done
Logika untuk melewatkan file biner adalah dari posting ini .
CATATAN:
expand
mengingat bahwa keduanya POSIX? Misalnya apakah ada opsi perubahan sebaris? Git safety at: stackoverflow.com/a/52136507/895245
Bagaimana saya bisa mengonversi tab menjadi spasi di setiap file direktori (mungkin secara rekursif)?
Ini biasanya bukan yang Anda inginkan.
Apakah Anda ingin melakukan ini untuk gambar png? File PDF? Direktori .git? Anda
Makefile
(yang membutuhkan tab)? Dump SQL 5GB?
Secara teori, Anda bisa meneruskan banyak opsi pengecualian ke find
atau apa pun yang Anda gunakan; tetapi ini rapuh, dan akan pecah segera setelah Anda menambahkan file biner lainnya.
Yang Anda inginkan, setidaknya:
expand
apakah ini, sed
tidak).Sejauh yang saya tahu, tidak ada utilitas Unix "standar" yang dapat melakukan ini, dan itu tidak mudah dilakukan dengan shell one-liner, jadi diperlukan skrip.
Beberapa waktu yang lalu saya membuat skrip kecil bernama
sanitize_files yang melakukan hal itu. Juga perbaikan beberapa hal umum lainnya seperti mengganti \r\n
dengan \n
, menambahkan Trailing \n
, dll
Anda dapat menemukan skrip yang disederhanakan tanpa fitur tambahan dan argumen baris perintah di bawah ini, tetapi saya sarankan Anda menggunakan skrip di atas karena lebih mungkin untuk menerima perbaikan bug dan pembaruan lainnya daripada posting ini.
Saya juga ingin menunjukkan, dalam menanggapi beberapa jawaban lain di sini, bahwa menggunakan shell globbing bukanlah cara yang kuat untuk melakukan ini, karena cepat atau lambat Anda akan berakhir dengan lebih banyak file daripada yang akan cocok ARG_MAX
(pada modern Sistem Linux 128k, yang mungkin tampak banyak, tetapi cepat atau lambat itu tidak
cukup).
#!/usr/bin/env python
#
# http://code.arp242.net/sanitize_files
#
import os, re, sys
def is_binary(data):
return data.find(b'\000') >= 0
def should_ignore(path):
keep = [
# VCS systems
'.git/', '.hg/' '.svn/' 'CVS/',
# These files have significant whitespace/tabs, and cannot be edited
# safely
# TODO: there are probably more of these files..
'Makefile', 'BSDmakefile', 'GNUmakefile', 'Gemfile.lock'
]
for k in keep:
if '/%s' % k in path:
return True
return False
def run(files):
indent_find = b'\t'
indent_replace = b' ' * indent_width
for f in files:
if should_ignore(f):
print('Ignoring %s' % f)
continue
try:
size = os.stat(f).st_size
# Unresolvable symlink, just ignore those
except FileNotFoundError as exc:
print('%s is unresolvable, skipping (%s)' % (f, exc))
continue
if size == 0: continue
if size > 1024 ** 2:
print("Skipping `%s' because it's over 1MiB" % f)
continue
try:
data = open(f, 'rb').read()
except (OSError, PermissionError) as exc:
print("Error: Unable to read `%s': %s" % (f, exc))
continue
if is_binary(data):
print("Skipping `%s' because it looks binary" % f)
continue
data = data.split(b'\n')
fixed_indent = False
for i, line in enumerate(data):
# Fix indentation
repl_count = 0
while line.startswith(indent_find):
fixed_indent = True
repl_count += 1
line = line.replace(indent_find, b'', 1)
if repl_count > 0:
line = indent_replace * repl_count + line
data = list(filter(lambda x: x is not None, data))
try:
open(f, 'wb').write(b'\n'.join(data))
except (OSError, PermissionError) as exc:
print("Error: Unable to write to `%s': %s" % (f, exc))
if __name__ == '__main__':
allfiles = []
for root, dirs, files in os.walk(os.getcwd()):
for f in files:
p = '%s/%s' % (root, f)
if do_add:
allfiles.append(p)
run(allfiles)
Saya suka contoh "temukan" di atas untuk aplikasi rekursif. Untuk mengadaptasinya menjadi non-rekursif, hanya mengubah file di direktori saat ini yang cocok dengan wildcard, ekspansi glob shell bisa mencukupi untuk sejumlah kecil file:
ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v
Jika Anda ingin itu diam setelah Anda percaya bahwa itu berfungsi, cukup letakkan -v
pada sh
perintah di akhir.
Tentu saja Anda dapat memilih set file dalam perintah pertama. Misalnya, daftar hanya subdirektori tertentu (atau direktori) dengan cara yang terkontrol seperti ini:
ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
Atau pada gilirannya jalankan temukan (1) dengan beberapa kombinasi parameter kedalaman dll:
find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
ARG_MAX
panjang. Ini 128k pada sistem Linux, tetapi saya telah menemui batas ini cukup kali untuk tidak bergantung pada shell globbing.
find
dapat dikatakan -maxdepth 1
, dan hanya memproses entri direktori yang sedang dimodifikasi, bukan seluruh pohon.
Saya digunakan astyle
untuk indentasi ulang semua kode C / C ++ saya setelah menemukan tab dan spasi campuran. Ia juga memiliki opsi untuk memaksa gaya penyangga tertentu jika Anda mau.
Satu dapat digunakan vim
untuk itu:
find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
Seperti yang dinyatakan oleh Carpetsmoker, itu akan melakukan retab sesuai dengan vim
pengaturan Anda . Dan modelines dalam file, jika ada. Juga, itu akan mengganti tab tidak hanya di awal baris. Yang bukan apa yang biasanya Anda inginkan. Misalnya, Anda mungkin memiliki literal, yang berisi tab.
:retab
akan mengubah semua tab dalam file, bukan yang di awal. itu juga tergantung pada apa :tabstop
dan :expandtab
pengaturan Anda di vimrc atau modeline, jadi ini mungkin tidak berfungsi sama sekali.
tabstop
dan expandtab
, itu akan berhasil jika Anda menggunakan vim
. Kecuali jika Anda memiliki garis mode di file.
Rekomendasi saya adalah menggunakan:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
Komentar:
sed
adalah editor aliran. Gunakan ex
untuk mengedit di tempat. Ini menghindari membuat file temp tambahan dan spawning shells untuk setiap penggantian seperti pada jawaban teratas .find|xargs
sebagai ganti find -exec
. Seperti yang ditunjukkan oleh @ gniourf-gniourf ini menyebabkan masalah dengan spasi, tanda kutip dan karakter kontrol dalam nama file cf. Wheeler .ex
mungkin tidak tersedia di setiap sistem Unix. Mengganti dengan vi -e
mungkin bekerja pada lebih banyak mesin. Juga, regex Anda menggantikan sejumlah karakter tab awal dengan dua spasi. Ganti regex dengan +%s/\t/ /g
tidak merusak lekukan multi level. Namun ini juga memengaruhi karakter tab yang tidak digunakan untuk indentasi.
/\t/ /
varian pada file saya, tetapi memilih untuk /\t\+//
tidak merusak tab yang tidak membuat indentasi. Merindukan masalah dengan lekukan multi! Memperbarui Jawaban. [1] man7.org/linux/man-pages/man1/ex.1p.html#SEE%C2%A0ALSO
xargs
cara ini tidak berguna, tidak efisien dan rusak (pikirkan nama file yang mengandung spasi atau kutipan). Mengapa Anda tidak menggunakan find
's -exec
beralih bukan?
-print0
opsi untuk menemukan / xargs. Saya suka xargs -exec
sejak: a) Pemisahan masalah b) dapat ditukar dengan GNU paralel lebih mudah.
Untuk mengonversi semua file Java secara rekursif dalam direktori menggunakan 4 spasi alih-alih tab:
find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
Anda dapat menggunakan find
dengan tabs-to-spaces
paket untuk ini.
Pertama, instal tabs-to-spaces
npm install -g tabs-to-spaces
kemudian, jalankan perintah ini dari direktori root proyek Anda;
find . -name '*' -exec t2s --spaces 2 {} \;
Ini akan mengganti setiap tab
karakter dengan 2 spaces
di setiap file.
Tidak ada badan yang disebutkan rpl
? Menggunakan rpl Anda dapat mengganti string apa pun. Untuk mengonversi tab menjadi spasi,
rpl -R -e "\t" " " .
sangat sederhana.
Penggunaan expand
seperti yang disarankan dalam jawaban lain tampaknya merupakan pendekatan yang paling logis untuk tugas ini saja.
Yang mengatakan, itu juga dapat dilakukan dengan Bash dan Awk jika Anda mungkin ingin melakukan beberapa modifikasi lain bersamanya.
Jika menggunakan Bash 4.0 atau lebih besar, shopt builtin globstar
dapat digunakan untuk mencari secara rekursif **
.
Dengan GNU Awk versi 4.1 atau lebih tinggi, modifikasi file seperti "inplace" dapat dibuat:
shopt -s globstar
gawk -i inplace '{gsub("\t"," ")}1' **/*.ext
Jika Anda ingin mengatur jumlah spasi per tab:
gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext
Unduh dan jalankan skrip berikut untuk secara rekursif mengonversi tab keras menjadi tab lunak dalam file teks biasa.
Jalankan skrip dari dalam folder yang berisi file teks biasa.
#!/bin/bash
find . -type f -and -not -path './.git/*' -exec grep -Iq . {} \; -and -print | while read -r file; do {
echo "Converting... "$file"";
data=$(expand --initial -t 4 "$file");
rm "$file";
echo "$data" > "$file";
}; done;
Metode ramah repositori
git-tab-to-space() (
d="$(mktemp -d)"
git grep --cached -Il '' | grep -E "${1:-.}" | \
xargs -I'{}' bash -c '\
f="${1}/f" \
&& expand -t 4 "$0" > "$f" && \
chmod --reference="$0" "$f" && \
mv "$f" "$0"' \
'{}' "$d" \
;
rmdir "$d"
)
Bertindak pada semua file di bawah direktori saat ini:
git-tab-to-space
Bertindak hanya pada file C atau C ++:
git-tab-to-space '\.(c|h)(|pp)$'
Anda mungkin menginginkan ini terutama karena Makefile yang mengganggu yang memerlukan tab.
Perintah git grep --cached -Il ''
:
.git
seperti yang dijelaskan di: Bagaimana cara mendaftar semua file teks (non-biner) dalam repositori git?
chmod --reference
membuat izin file tidak berubah: /unix/20645/clone-ownership-and-permissions-from-another-file Sayangnya saya tidak dapat menemukan alternatif POSIX yang ringkas .
Jika basis kode Anda memiliki ide gila untuk mengizinkan tab mentah fungsional dalam string, gunakan:
expand -i
dan kemudian bersenang-senang memeriksa semua tab awal yang tidak dimulai satu per satu, yang dapat Anda daftarkan: Apakah mungkin untuk mendapatkan grep untuk tab?
Diuji pada Ubuntu 18.04.
Mengonversi tab menjadi spasi hanya dalam file ".lua" [tab -> 2 spasi]
find . -iname "*.lua" -exec sed -i "s#\t# #g" '{}' \;
expand -t 4 input >output
)
expand -t 4
akan memperluas tab a\tb
ke 3 spasi dan tab aa\tb
ke 2 spasi, seperti yang seharusnya. expand
mempertimbangkan konteks tab, sed
tidak dan akan mengganti tab dengan jumlah spasi yang Anda tentukan, terlepas dari konteksnya.
Gunakan vim-way:
$ ex +'bufdo retab' -cxa **/*.*
globstar
( **
) untuk rekursi, aktifkan oleh shopt -s globstar
.**/*.c
.Untuk memodifikasi tabstop, tambahkan +'set ts=2'
.
Namun sisi buruknya adalah ia dapat mengganti tab di dalam string .
Jadi untuk solusi yang sedikit lebih baik (dengan menggunakan substitusi), coba:
$ ex -s +'bufdo %s/^\t\+/ /ge' -cxa **/*.*
Atau dengan menggunakan ex
editor + expand
utility:
$ ex -s +'bufdo!%!expand -t2' -cxa **/*.*
Untuk spasi tambahan, lihat: Bagaimana menghapus spasi spasi tambahan untuk banyak file?
Anda dapat menambahkan fungsi berikut ke .bash_profile
:
# Convert tabs to spaces.
# Usage: retab *.*
# See: https://stackoverflow.com/q/11094383/55075
retab() {
ex +'set ts=2' +'bufdo retab' -cxa $*
}
:retab
mungkin tidak bekerja sama sekali , shell globbing adalah solusi yang buruk untuk hal semacam ini , Anda :s
perintah akan menggantikan setiap jumlah tab dengan 2 spasi (yang Anda hampir tidak pernah mau), memulai ex hanya untuk menjalankan suatu :!expand
proses itu konyol ...