Script để xóa khoảng trắng và chữ thường trong tên tệp


7

Tôi đang cố gắng viết một tập lệnh sẽ thay thế khoảng trắng bằng "-" và làm cho tất cả các chữ cái viết thường cho tất cả các tệp trong thư mục hiện tại.

for x in 'ls'
    do
        if [ ! -f $x ]; then
            continue
        fi

        lc = `echo $x | tr '[A-Z]' '[a-z]'`
        if [ $lc != $x ]; then
            mv $x $lc
        fi
    done

find -name "* *" -type f | rename 's/ /-/g'

Tôi nhận được kết quả đầu ra sau: call: đổi tên từ thành tập tin ...

Tuy nhiên, tên không thay đổi, ví dụ: 252680610243-Analyzed Sample2 2Jul12.txt

Tôi đã thay đổi các quyền với chmod 706, điều này sẽ gây ra vấn đề? Tôi đang thiếu gì ở đây?

Đây là đầu ra của bash -x lower.sh:

+ for x in ''\''ls'\'''
+ '[' '!' -f ls ']'
+ continue
+ find -name '* *' -type f
+ rename 's/ /-/g'
call: rename from to files...

1
Tại sao bạn không bình luận mvvà ném vào một echo $lchoặc hai để bạn có thể thấy những gì bạn đang làm, sau đó làm việc từ đó? Xem thêm bash -x: tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03.html Học cách câu cá tốt hơn là yêu cầu cá;)
goldilocks

Bạn có thể sử dụng các biểu thức thông thường cho các ký tự hạ thấp mà không cần sử dụng tr. Xem stackoverflow.com/questions/689495/iêu
Erik

Tôi biết cách regex để làm điều này, tôi chỉ đang cố gắng để trở nên quen thuộc hơn với kịch bản bash. Xem OP để cập nhật. Điều thú vị là thay thế mv...bằng echo $lckhông thay đổi đầu ra của mã.
Joe

1
Tôi nghĩ rằng bạn muốn `ls`không 'ls'- hoặc tốt hơn, chỉ for x in *. Quan điểm của tôi về tiếng vang và công cụ là bạn nên xem xét đầu ra của mỗi lệnh (ví dụ: hoàn toàn for x in 'ls'không phải là những gì bạn nghĩ).
goldilocks

Đó là một lỗi, bây giờ là đâyUnexpected arguments passed on cmd line ./lower.sh: line 8: [: !=: unary operator expected
Joe

Câu trả lời:


8

Trên Debian và các công cụ phái sinh (bao gồm Ubuntu), điều này thật dễ dàng với một cuộc gọi đơn giản đến rename:

$ touch 'A B'
$ rename 'tr/ A-Z/-a-z/' -- *
$ ls
a-b

Nhưng bạn renamelà một tiện ích khác nhau có cùng tên .


Tôi hoàn toàn quên mất việc đổi tên lệnh :)
TP

Điều này không hiệu quả với tôi
Joe

Có lẽ bạn đang sử dụng đổi tên từ linux-linux chứ không phải là giao hàng với perl.
Dennis Kaarsemaker

1
Lưu ý rằng nó sẽ chỉ dịch các chữ cái ASCII, không phải các chữ cái khác trong miền địa phương hiện tại nếu miền địa phương đó không dựa trên ASCII.
Stéphane Chazelas

Hmm ... Tôi tự hỏi nếu đổi tên perl chấp nhận lc làm đối số. Lc của Perl sẽ thực hiện các chữ cái không phải là ascii.
Dennis Kaarsemaker

6

Có một số vấn đề với kịch bản của bạn.

for x in 'ls'

Bạn đang lặp lại một danh sách chứa một từ : ls. Bạn có thể có nghĩa là để viết `ls`(với backticks), để phân tích đầu ra của ls. Không phân tích đầu ra củals : điều này sẽ không hoạt động nếu tên tệp chứa các ký tự đặc biệt như khoảng trắng. Shell đã có sẵn một cách liệt kê các tệp trong một thư mục: ký tự đại diện. Vì vậy, viết for x in *.

        if [ ! -f $x ]; then

Điều này sẽ không hoạt động nếu tên tệp chứa khoảng trắng và các ký tự đặc biệt khác, bởi vì khi bạn viết $xdấu ngoặc kép bên ngoài, kết quả được chia thành các từ riêng biệt (và mỗi từ được hiểu là một mẫu ký tự đại diện, để khởi động). Để tránh hiệu ứng này, hãy đặt $xtrong dấu ngoặc kép : if [ ! -f "$x" ]; then. Bạn có thể nhớ các quy tắc phức tạp hoặc chỉ luôn luôn sử dụng dấu ngoặc kép xung quanh các thay thế khác nhau.

Một số dòng khác bị hỏng do thiếu dấu ngoặc kép. Chỉ cần đặt chúng xung quanh mỗi thay thế biến.

       lc = `echo $x | tr '[A-Z]' '[a-z]'`

Điều này sẽ không hoạt động do các khoảng trắng xung quanh dấu bằng: dòng này thực thi lclệnh, chuyển nó =làm đối số đầu tiên (và các đối số khác). Một bài tập trong shell phải không có khoảng trắng xung quanh =dấu.

Bạn không cần dấu ngoặc quanh các đối số của tr. Đây là lệnh xảy ra với công việc bởi vì bạn đang nói để thay thế [bằng []bằng cách ]thêm vào các lowercasing.

Tôi khuyên bạn nên sử dụng dấu ngoặc đơn $(…)thay vì backticks `…`để thay thế lệnh. Chúng tương đương nhau, ngoại trừ việc backticks có các quy tắc phức tạp khi trích dẫn những thứ bên trong nó, trong khi $(…)có một cú pháp rất trực quan. Nói chung, lệnh đó phải là:lc=$(echo "$x" | tr A-Z a-z)

find -name "* *" -type f | rename 's/ /-/g'

Thông báo lỗi của bạn cho thấy rằng bạn có renamelệnh từ gói linux-linux chứ không phải tập lệnh Perl được tìm thấy trên Debian và các công cụ phái sinh (bao gồm cả Ubuntu). Cú pháp bạn sử dụng chỉ có ý nghĩa với tập lệnh Perl.

Nếu bạn sẽ sử dụng tập lệnh Perl, nó có thể thay đổi trường hợp thành chữ thường, vì vậy bạn không cần phải làm điều đó trước. Và bạn không cần phải gọi findtrừ khi bạn muốn đổi tên các tệp trong thư mục con (phần đầu tiên của bạn không xử lý). Nếu bạn muốn đổi tên tất cả các tệp trong thư mục hiện tại, bạn chỉ cần viết:

prename 'tr/A-Z /a-z-/' -- *

Nếu bạn chỉ muốn đổi tên các tệp thông thường (nhưng không phải thư mục con, liên kết tượng trưng, ​​v.v.), hãy sử dụng find:

find . -type f -maxdepth 1 -exec prename 'tr/A-Z /a-z-/' {} +

Nếu bạn cũng muốn hành động trên các tệp trong thư mục con và tên thư mục có thể chứa dấu cách hoặc chữ in hoa, bạn phải cẩn thận để chúng một mình. Vì bạn đang dùng Linux, bạn có thể sử dụng -execdirđể gọi prenamevới một đối số không chứa tên thư mục.

find . -type f -execdir prename 'tr/A-Z /a-z-/' {} +

Nếu bạn không có renametiện ích Perl thì sao? Tiện ích linux-linux renamesẽ không giúp bạn ở đây. Bạn có thể đặt thêm một thay thế trong vòng lặp của bạn.

for x in ./*; do
  if [ -f "$x" ]; then
    y=$(echo "$x" | tr 'A-Z-' 'a-z ')
    mv "$x" "$y"
  fi
done

Với bash, bạn không cần gọi tr, bạn có thể sử dụng các cấu trúc thay thế chuỗi của nó.

for x in ./*; do
  if [ -f "$x" ]; then
    lc=${x,,}            # convert all letters to lowercase
    y=${lc// /-}         # replace spaces by hyphens
    if [ "$x" != "$y" ]; then
      mv "$x" "$y"
    fi
  fi
done

Nếu bạn cũng muốn hành động trên các tệp trong thư mục con, bạn có thể sử dụng một quả cầu đệ quy (được bật bởi globstartùy chọn). Một lần nữa, hãy cẩn thận để lại phần thư mục nếu chúng có thể chứa các chữ cái hoặc dấu cách.

shopt -s globstar
for x in ./**/*; do
  if [ -f "$x" ]; then
    old_basename=${x##*/}
    new_basename=${old_basename,,}
    new_basename=${new_basename// /-}
    if [ "$new_basename" != "$old_basename" ]; then
      mv "${x%/*}/$old_basename" "${x%/*}/$new_basename"
    fi
  fi
done

Trong zsh, đặt autoload -U zmvvào của bạn .zshrcvà bạn có thể sử dụng zmvvới vòng loại toàn cầu . để chỉ khớp với các tệp thông thường và cấu trúc mở rộng tham số cho việc đổi tên:

zmv -Q '*(.)' '${(L)f// /-}'

Để hành động trên các tệp trong thư mục con là tốt:

zmv -Qw '**/*(.)' '$1${(L)2// /-}'

Với find -exec prename, bạn chỉ cần dịch basename, nếu không, nó sẽ không hoạt động chính xác nếu một số tên có chữ hoa. Không phải tất cả các giải pháp đều hoạt động giống nhau đối với các chữ cái không phải ASCII.
Stéphane Chazelas

@StephaneChazelas Đúng, ý tôi là thêm -maxdepth 1kịch bản gốc trong câu hỏi là đệ quy, nhưng tôi quên mất. Câu trả lời là đủ dài, tôi đang dính vào các chữ cái ASCII giống như câu hỏi.
Gilles 'SO- ngừng trở nên xấu xa'

Cũng lưu ý rằng SysV truyền thống tryêu cầu [...], vì vậy người ta có thể muốn viết nó tr '[A-Z]' '[a-z]'để cải thiện tính di động (như là phần bổ sung [, ]không gây hại cho kiểu BSD / POSIX tr).
Stéphane Chazelas

3

Vấn đề chính là bạn không lặp lại các tệp của mình: bạn đang lặp qua một chuỗi , "ls". Bạn có thể có ý định sử dụng backticks thay vì dấu ngoặc đơn. Tuy nhiên, đừng phân tíchls .

Bạn cũng cần trích dẫn các biến của mình đặc biệt là vì bạn biết chúng chứa khoảng trắng.

Chỉ trong bash, bạn có thể làm điều này:

declare -l lc    # whatever is stored in $lc is automatically lower cased
for x in *; do
    lc=${x//[[:space:]]/}  # replace all whitespace with nothing
    [ "$lc" != "$x" ] && mv -- "$x" "$lc"
done

2

Bạn có thể gặp vấn đề với lệnh "mv" và dấu cách. Tôi bao quanh các tập tin ban đầu với dấu ngoặc kép.

Kịch bản này đã làm việc cho tôi.

#!/bin/bash

for f in *
do
    g=$(echo $f | sed -e 's/\s/\-/g' | tr A-Z a-z)
    mv "$f" $g
done

2

Trong Bash 4 $ {var ,,} chuyển đổi var thành chữ thường:

for f in *; do f2=${f,,}; mv "$f" "${f2// /-}"; done
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.