Làm cách nào tôi có thể chuyển đổi các tab thành khoảng trắng trong mỗi tệp của một thư mục (có thể là đệ quy)?
Đây thường không phải là những gì bạn muốn.
Bạn có muốn làm điều này cho hình ảnh png? Tập tin PDF? Thư mục .git? Của bạn
Makefile
( yêu cầu các tab)? Kết xuất SQL 5GB?
Về lý thuyết, bạn có thể vượt qua rất nhiều tùy chọn loại trừ find
hoặc bất cứ thứ gì bạn đang sử dụng; nhưng điều này rất dễ hỏng và sẽ bị hỏng ngay khi bạn thêm các tệp nhị phân khác.
Những gì bạn muốn, ít nhất là:
- Bỏ qua các tập tin trên một kích thước nhất định.
- Phát hiện nếu một tệp là nhị phân bằng cách kiểm tra sự hiện diện của byte NULL.
- Chỉ thay thế các tab khi bắt đầu một tệp (
expand
thực hiện điều này, sed
không).
Theo như tôi biết, không có tiện ích Unix "tiêu chuẩn" nào có thể làm được điều này và nó không dễ thực hiện với trình bao một lớp vỏ, vì vậy cần có một kịch bản.
Cách đây một thời gian, tôi đã tạo ra một đoạn script nhỏ gọi là
sanitize_files , chính xác là nó. Nó cũng sửa một số nội dung phổ biến khác như thay thế \r\n
bằng \n
, thêm dấu \n
, v.v.
Bạn có thể tìm thấy một tập lệnh được đơn giản hóa mà không có các tính năng bổ sung và các đối số dòng lệnh bên dưới, nhưng tôi khuyên bạn nên sử dụng tập lệnh trên vì nó có nhiều khả năng nhận được các lỗi và cập nhật khác hơn bài đăng này.
Tôi cũng muốn chỉ ra, để đáp lại một số câu trả lời khác ở đây, rằng sử dụng shell globalbing không phải là một cách mạnh mẽ để làm điều này, bởi vì sớm hay muộn bạn sẽ kết thúc với nhiều tệp hơn sẽ phù hợp ARG_MAX
(trên hiện đại Các hệ thống Linux có giá 128 nghìn, có vẻ rất nhiều, nhưng sớm hay muộn thì không
đủ).
#!/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)