Mengkonversi format output ls -l ke format chmod

17

Katakanlah saya memiliki output berikut dari ls -l:

drwxr-xr-x 2 root root 4096 Apr  7 17:21 foo

Bagaimana saya bisa mengonversikan ini secara otomatis ke format yang digunakan oleh chmod?

Sebagai contoh:

$ echo drwxr-xr-x | chmod-format
755

Saya menggunakan OS X 10.8.3.

Tyilo
sumber
2
Lebih mudah dengan stat. Apakah kamu memilikinya? (Ini alat GNU, jadi sebagian besar tersedia di Linux, bukan di Unix.)
manatwork
@manatwork stat foomemberi 16777219 377266 drwxr-xr-x 119 Tyilo staff 0 4046 "Apr 7 17:49:03 2013" "Apr 7 18:08:31 2013" "Apr 7 18:08:31 2013" "Nov 25 17:13:52 2012" 4096 0 0 /Users/Tyilo. Saya tidak melihat 755di dalamnya.
Tyilo

Jawaban:

24

Beberapa sistem memiliki perintah untuk menampilkan izin file sebagai angka, tetapi sayangnya, tidak ada yang portabel.

zshmemiliki stat(alias zstat) builtin dalam statmodul:

zmodload zsh/stat
stat -H s some-file

Kemudian, modeis in $s[mode]tetapi adalah mode, yaitu ketik + perms.

Jika Anda ingin izin dinyatakan dalam oktal, Anda perlu:

perms=$(([##8] s[mode] & 8#7777))

BSD (termasuk Apple OS / X ) juga memiliki statperintah.

mode=$(stat -f %p some-file)
perm=$(printf %o "$((mode & 07777))"

GNU find (dari tahun 1990 dan mungkin sebelumnya) dapat mencetak izin sebagai oktal:

find some-file -prune -printf '%m\n'

Kemudian (2001, jauh setelah zsh stat(1997) tetapi sebelum BSD stat(2002)) sebuah statperintah GNU diperkenalkan dengan sintaks yang berbeda lagi:

stat -c %a some-file

Jauh sebelum itu, IRIX sudah memiliki statperintah (sudah ada di IRIX 5.3 pada tahun 1994) dengan sintaks lain:

stat -qp some-file

Sekali lagi, ketika tidak ada perintah standar, taruhan terbaik untuk portabilitas adalah dengan menggunakan perl:

perl -e 'printf "%o\n", (stat shift)[2]&07777' some-file
Stéphane Chazelas
sumber
15

Anda dapat meminta GNU statuntuk mengeluarkan izin dalam format oktal dengan menggunakan -copsi. Dari man stat:

       -c  --format=FORMAT
              use the specified FORMAT instead of the default; output a
              newline after each use of FORMAT
⋮
       %a     access rights in octal
⋮
       %n     file name

Jadi dalam kasus Anda:

bash-4.2$ ls -l foo
-rw-r--r-- 1 manatwork manatwork 0 Apr  7 19:43 foo

bash-4.2$ stat -c '%a' foo
644

Atau Anda bahkan dapat mengotomatisasi dengan memformat statoutput sebagai perintah yang valid:

bash-4.2$ stat -c "chmod %a '%n'" foo
chmod 644 'foo'

bash-4.2$ stat -c "chmod %a '%n'" foo > setpermission.sh

bash-4.2$ chmod a= foo

bash-4.2$ ls -l foo
---------- 1 manatwork manatwork 0 Apr  7 19:43 foo

bash-4.2$ sh setpermission.sh 

bash-4.2$ ls -l foo
-rw-r--r-- 1 manatwork manatwork 0 Apr  7 19:43 foo

Solusi di atas juga akan berfungsi untuk banyak file jika menggunakan wildcard:

stat -c "chmod -- %a '%n'" -- *

Akan bekerja dengan benar dengan nama file yang mengandung karakter spasi, tetapi akan gagal pada nama file yang mengandung tanda kutip tunggal.

manatwork
sumber
2
Saya stattidak punya -cpilihan. Saya menggunakan OS X 10.8.3.
Tyilo
Terima kasih atas informasinya, @ Tyilo. Dan maaf, saya tidak dapat membantu dengan alat OS X.
manatwork
Coba baca halaman manual ^ W ^ W ^ W stat (1) pada Mac OS X memiliki flag -f untuk menentukan format output, misalnyastat -f 'chmod %p "%N"'
gelraen
11

Untuk mengkonversi dari notasi simbolis ke oktal, saya pernah muncul dengan:

chmod_format() {
  sed 's/.\(.........\).*/\1/
    h;y/rwsxtSTlL-/IIIIIOOOOO/;x;s/..\(.\)..\(.\)..\(.\)/|\1\2\3/
    y/sStTlLx-/IIIIIIOO/;G
    s/\n\(.*\)/\1;OOO0OOI1OIO2OII3IOO4IOI5IIO6III7/;:k
    s/|\(...\)\(.*;.*\1\(.\)\)/\3|\2/;tk
    s/^0*\(..*\)|.*/\1/;q'
}

Diperluas:

#! /bin/sed -f
s/.\(.........\).*/\1/; # extract permissions and discard the rest

h; # store a copy on the hold space

# Now for the 3 lowest octal digits (rwx), translates the flags to
# binary where O means 0 and I means 1.
# l, L are for mandatory locking (a regular file that has 02000 on
# and not 010 on some systems like Linux). Some ls implementations
# like GNU ls confusingly use S there like for directories even though 
# it has nothing to do with setgid in that case. Some ls implementations 
# use L, some others l (against POSIX which requires an uppercase
# flag for extra flags when the execution bit is not set).
y/rwsxtSTlL-/IIIIIOOOOO/

x; # swap hold and pattern space, to do a second processing on those flags.

# now only consider the "xXlLsStT" bits:
s/..\(.\)..\(.\)..\(.\)/|\1\2\3/

y/sStTlLx-/IIIIIIOO/; # make up the 4th octal digit as binary like before

G; # append the hold space so we now have all 4 octal digits as binary

# remove the extra newline and append a translation table
s/\n\(.*\)/\1;OOO0OOI1OIO2OII3IOO4IOI5IIO6III7/

:k
  # translate the OOO -> 0 ... III -> 7 in a loop
  s/|\(...\)\(.*;.*\1\(.\)\)/\3|\2/
tk

# trim leading 0s and our translation table.
s/^0*\(..*\)|.*/\1/;q

Itu mengembalikan angka oktal dari output ls -lpada satu file.

$ echo 'drwSr-sr-T' | chmod_format
7654
Stéphane Chazelas
sumber
Saya menggunakan ini pada output dpkguntuk mengatur izin kembali ke "sebagaimana diinstal". Terima kasih telah menjawab pertanyaan literal tanpa memperhatikan perintah yang menghasilkan string izin.
HiTechHiTouch
3

Perintah ini pada Mac di bawah sh

stat -f "%Lp %N" your_files

jika Anda hanya menginginkan izin numerik, gunakan% Lp saja.

sebagai contoh:

stat -f "%Lp %N" ~/Desktop
700 Desktop

700 adalah izin numerik yang dapat digunakan dalam chmod, dan Desktop adalah nama file.

TonyL2ca
sumber
2

Inilah jawaban untuk pertanyaan Y (mengabaikan pertanyaan X ), terinspirasi oleh upaya OP:

#!/bin/bash
LC_COLLATE=C
while read ls_out
do
        extra=0
        perms=0
        for i in {1..9}
        do
                # Shift $perms to the left one bit, so we can always just add the LSB.
                let $((perms*=2))
                this_char=${ls_out:i:1}
                # If it's different from its upper case equivalent,
                # it's a lower case letter, so the bit is set.
                # Unless it's "l" (lower case L), which is special.
                if [ "$this_char" != "${this_char^}" ]  &&  [ "$this_char" != "l" ]
                then
                        let $((perms++))
                fi
                # If it's not "r", "w", "x", or "-", it indicates that
                # one of the high-order (S/s=4000, S/s/L/l=2000, or T/t=1000) bits
                # is set.
                case "$this_char" in
                  ([^rwx-])
                        let $((extra += 2 ** (3-i/3) ))
                esac
        done
        printf "%o%.3o\n" "$extra" "$perms"
done

Di atas mengandung beberapa bashism. Versi berikut ini tampaknya kompatibel dengan POSIX:

#!/bin/sh
LC_COLLATE=C
while read ls_out
do
        extra=0
        perms=0
        for i in $(seq 1 9)
        do
                # Shift $perms to the left one bit, so we can always just add the LSB.
                : $((perms*=2))
                this_char=$(expr "$ls_out" : ".\{$i\}\(.\)")
                # Lower case letters other than "l" indicate that permission bits are set.
                # If it's not "r", "w", "x", or "-", it indicates that
                case "$this_char" in
                  (l)
                        ;;
                  ([a-z])
                        : $((perms+=1))
                esac
                # If it's not "r", "w", "x", or "-", it indicates that
                # one of the high-order (S/s=4000, S/s/L/l=2000, or T/t=1000) bits
                # is set.
                case "$this_char" in
                  ([!rwx-])
                        : $((extra += 1 << (3-i/3) ))
                esac
        done
        printf "%o%.3o\n" "$extra" "$perms"
done

Catatan:

  • The LC_COLLATE=Cmenceritakan shell surat memperlakukan pola urutan rentang dengan menggunakan urutan ASCII, sehingga [a-e]setara dengan [abcde]. Di beberapa lokal (misalnya, en_US), [a-e]setara dengan [aAbBcCdDeE] (yaitu, [abcdeABCDE]) atau mungkin [abcdeABCD]- lihat Mengapa pernyataan bash case tidak peka terhadap huruf besar-kecil ...? )
  • Dalam versi kedua (yang kompatibel dengan POSIX):

    • casePernyataan pertama dapat ditulis ulang:

              case "$this_char" in
                ([a-km-z])
                      : $((perms+=1))
              esac
      

      tapi saya pikir cara saya sekarang membuatnya lebih mudah untuk melihat bahwa l surat itu ditangani secara berbeda. Atau, dapat ditulis ulang:

              case "$this_char" in
                ([rwxst])
                      : $((perms+=1))
              esac
      

      sejak r, w, x, s, dan tadalah satu-satunya huruf yang harus pernah muncul dalam string modus (selain l).

    • casePernyataan kedua dapat ditulis ulang:

              case "$this_char" in
                ([rwx])
                      ;;
                ([A-Za-z])
                      : $((extra += 1 << (3-i/3) ))
               esac
      

      untuk menegakkan aturan bahwa hanya huruf yang valid untuk menentukan bit mode. (Sebaliknya, versi yang lebih ringkas dalam skrip lengkap adalah malas, dan akan menerima -rw@rw#rw%setara dengan  rwSrwSrwT.) Atau, dapat ditulis ulang:

              case "$this_char" in
                ([SsTtLl])
                      : $((extra += 1 << (3-i/3) ))
              esac
      

      sejak S, s, T, t, L, dan ladalah satu-satunya huruf yang harus pernah muncul dalam modus tali (selain r, w, dan x).

Pemakaian:

$ echo drwxr-xr-x | chmod-format
0755
$ echo -rwsr-sr-x | chmod-format
6755
$ echo -rwSr-Sr-- | chmod-format
6644
$ echo -rw-r-lr-- | chmod-format
2644
$ echo ---------- | chmod-format
0000

Dan, ya, saya tahu lebih baik tidak menggunakan echoteks yang mungkin dimulai dengan -; Saya hanya ingin menyalin contoh penggunaan dari pertanyaan. Perhatikan, jelas, bahwa ini mengabaikan karakter 0 (yaitu, yang memimpin d/ b/ c/ -/ l/ p/ s/ D) dan 10 ( +/ ./ @). Ini mengasumsikan bahwa pengelola lstidak akan pernah mendefinisikan r/ Ratau w/ Wsebagai karakter yang valid di posisi ketiga, keenam, atau kesembilan (dan, jika mereka melakukannya, mereka harus dipukuli dengan tongkat ).


Juga, saya baru saja menemukan kode berikut, menurut cas , di bawah Cara mengembalikan kepemilikan grup / pengguna default semua file di bawah / var :

        let perms=0

        [[ "${string}" = ?r???????? ]]  &&  perms=$(( perms +  400 ))
        [[ "${string}" = ??w??????? ]]  &&  perms=$(( perms +  200 ))
        [[ "${string}" = ???x?????? ]]  &&  perms=$(( perms +  100 ))
        [[ "${string}" = ???s?????? ]]  &&  perms=$(( perms + 4100 ))
        [[ "${string}" = ???S?????? ]]  &&  perms=$(( perms + 4000 ))
        [[ "${string}" = ????r????? ]]  &&  perms=$(( perms +   40 ))
        [[ "${string}" = ?????w???? ]]  &&  perms=$(( perms +   20 ))
        [[ "${string}" = ??????x??? ]]  &&  perms=$(( perms +   10 ))
        [[ "${string}" = ??????s??? ]]  &&  perms=$(( perms + 2010 ))
        [[ "${string}" = ??????S??? ]]  &&  perms=$(( perms + 2000 ))
        [[ "${string}" = ???????r?? ]]  &&  perms=$(( perms +    4 ))
        [[ "${string}" = ????????w? ]]  &&  perms=$(( perms +    2 ))
        [[ "${string}" = ?????????x ]]  &&  perms=$(( perms +    1 ))
        [[ "${string}" = ?????????t ]]  &&  perms=$(( perms + 1001 ))
        [[ "${string}" = ?????????T ]]  &&  perms=$(( perms + 1000 ))

Saya telah menguji kode ini (tetapi tidak sepenuhnya), dan tampaknya berhasil, kecuali kenyataan bahwa ia tidak mengenali latau berada Ldi posisi keenam. Perhatikan, meskipun jawaban ini lebih unggul dalam hal kesederhanaan dan kejelasan, saya sebenarnya lebih pendek (hanya menghitung kode di dalam loop; kode yang menangani -rwxrwxrwxstring tunggal , tidak menghitung komentar), dan itu bisa dibuat lebih pendek dengan mengganti dengan .if condition; then …condition && …


Tentu saja, Anda tidak perlu menguraikan outputls .

Scott
sumber
@ StéphaneChazelas: Oke, saya katakan #!/bin/shdan kemudian menggunakan beberapa bashism. Ups. Tapi Anda melewatkan beberapa: dan tidak tampaknya POSIX baik (Standard tidak menyebutkan sama sekali, dan squirrelly pada dan ). Sebaliknya, saya tidak menggunakan ; yang muncul hanya dalam jawaban cas , yang saya kutip dari sini . Juga, jawaban saya tidak menangani 'l' dan 'L', dan saya sudah menunjukkan fakta bahwa jawaban cas tidak. $(( variable++ ))$(( number ** number ))**++-- [[…]]
Scott
Maaf tentang l / L [[, saya membaca terlalu cepat. Ya, - dan ++ bukan POSIX. POSIX memungkinkan shell untuk mengimplementasikannya, itu berarti Anda harus menulis $((- -a))jika Anda ingin negasi ganda, bukan yang Anda gunakan $((--a))untuk berarti operasi pengurangan.
Stéphane Chazelas
Perhatikan bahwa seqini bukan perintah POSIX. Anda mungkin dapat menggunakan operator $ {var #?} Untuk menghindari expr. Bukan berarti LC_COLLATE tidak akan menimpa LC_ALL
Stéphane Chazelas
@ StéphaneChazelas: OK, Anda berbicara tentang jawaban cas lagi sekarang, kan? Dia mengambil pendekatan "malas" menggunakan aritmatika desimal untuk membangun string yang terlihat seperti angka oktal. Perhatikan bahwa semua nilai langkahnya (4000, 2000, 1000, 400, 200, 100, 40, 20, dan 10) adalah angka desimal. Tetapi, karena tidak ada 8atau tidak 9, dan tidak ada cara untuk mendapatkan lebih dari 7pada posisi desimal apa pun, ia dapat menarik sandiwara itu. ... ... ... ... ... ... ... ... (Komentar ini adalah tanggapan atas komentar Stéphane Chazelas yang menghilang.)
Scott
Ya, saya menyadarinya nanti, itulah sebabnya saya menghapus komentar.
Stéphane Chazelas
1

Jika tujuan Anda adalah untuk mengambil izin dari satu file dan memberikannya ke yang lain juga, GNU chmodsudah memiliki opsi "referensi" untuk itu .

Bratchley
sumber
OP menyebutkan dia menggunakan Apple OS / X, jadi chmodtidak akan chmodada GNU di sana.
Stéphane Chazelas
Ah ya saya melihat komentar pada jawaban lain di mana mereka mengatakan platform mereka. Milik saya bukan satu-satunya yang menyebutkan GNU dan Anda bisa mendapatkan GNU Utilities di Mac OS X
Bratchley
0

Alternatif, jika Anda ingin menyimpan izin, mengembalikannya nanti, atau menggunakan file lain setfacl/getfacl, dan juga mengembalikan (POSIX-draft) ACL sebagai bonus.

getfacl some-file > saved-perms
setfacl -M saved-perms some-other-file

(pada Solaris, gunakan -fbukan -M).

Namun, meskipun mereka tersedia di beberapa BSD, mereka tidak ada di Apple OS / X di mana ACL hanya dimanipulasi chmod.

Stéphane Chazelas
sumber
0

Pada Mac OS X (10.6.8) Anda harus menggunakan stat -f format(karena itu sebenarnya NetBSD / FreeBSD stat).

# using Bash

mods="$(stat -f "%p" ~)"    # octal notation
mods="${mods: -4}"
echo "$mods"

mods="$(stat -f "%Sp" ~)"  # symbolic notation
mods="${mods: -9}"
echo "$mods"

Untuk hanya menerjemahkan string izin simbolis yang dihasilkan oleh ls -lmenjadi oktal (hanya menggunakan builtin shell) lihat: showperm.bash .

# from: showperm.bash
# usage: showperm modestring
#
# example: showperm '-rwsr-x--x'
phil32
sumber