LinuxShell腳本攻略--第二章 命令之樂


用 cat 進行拼接 
文件查找與文件列表
玩轉 xargs 
用 tr 進行轉換
排序
臨時文件命名與隨機數
分割文件和數據
根據擴展名切分文件名
mv 批量重命名文件
交互輸入自動化

cat:

echo 'Text through stdin' | cat - file.txt    #  - 被作為 stdin 文本的文件名。
$ cat -s file               #壓縮相鄰的空白行
$ cat -T file.py          #將制表符顯示為^I
$ cat -n lines.txt       #顯示行號

find:

$ find . -print
$ find .

這兩條命令效果是一樣的。都是查找當前目錄下的所有的文件及目錄,使用-print時‘\n’作為用於輸出的文件名進行分隔,-print0 指明了使用‘\0’ 進行分隔。

$ find /home/slynux -name "*.txt" -print       #按文件名查找
$ find . -iname "example*" -print              #按文件名不區分大小寫查找
$ find . \( -name "*.txt" -o -name "*.pdf" \) -print     # -o OR條件 ()將括號中的語句作為一個整體
$ find /home/users -path "*/slynux/*" -print # -name 用給定的文件名匹配,-path將文件路徑作為一個整體進行匹配。
$ find . -regex ".*\(\.py\|\.sh\)$" #正則表達式 查找.py或.sh文件
$ find . -iregex ".*\(\.py\|\.sh\)$" #不區分大小寫
$ find . ! -name "*.txt" -print # !否定。不已.txt結尾的文件
$ find . -maxdepth 1 -name "f*" -print #基於目錄的深度查找 -maxdepth -mindepth

在有 -maxdepth 或 -mindepth時,這兩個參數應該作為第三個參數出現。如果作為第4個或之后的參數,就可能會影響到 find 的效率,因為它不得不進行一些不必要的檢查。例如,如果 -maxdepth 作為第四個參數, -type 作為第三個參數, find首先會找出符合 -type 的所有文件,然后在所有匹配的文件中再找出符合指定深度的那些。但是如果反過來,目錄深度作為第三個參數, -type 作為第四個參數,那么 find 就能夠在找到所有符合指定深度的文件后,再檢查這些文件的類型,這才是最有效的搜索之道。
-type 按類型查找

文件類型       類型參數
普通文件         f
符號鏈接         l
目錄           d
字符設備         c
塊設備          b
套接字          s
FIFO             p

$ find . -type d -print    #列出所有的目錄

Unix/linux文件系統中每個文件都有三種時間戳:

訪問時間( -atime  ):用戶最近一次訪問文件的時間。
修改時間( -mtime ):文件內容最后一次被修改的時間。
變化時間( -ctime  ):文件元數據(例如權限或所有權)最后一次改變的時間。

-atime 、 -mtime 、 -ctime 可作為 find 的時間選項。它們可以用整數值指定,單位是天。這些整數值通常還帶有 - 或 + : - 表示小於, + 表示大於。

$ find . -type f -atime -7 -print        #打印出在最近7天內被訪問過的所有文件
$ find . -type f -atime 7 -print #打印出恰好在7天前被訪問過的所有文件
$ find . -type f -atime +7 -print     #打印出訪問時間超過7天的所有文件

-amin (訪問時間),-mmin (修改時間),-cmin (變化時間)  這些時間的計量單位是“ 分鍾 ”。

使用 -newer ,我們可以指定一個用於比較時間戳的參考文件,然后找出比參考文件更新的(更近的修改時間)所有文件

$ find . -type f -newer file.txt -print    #找出比file.txt修改時間更近的所有文件

基於文件大小的搜索:-size

$  find . -type f -size +2k        # 大於2KB的文件
  • b —— 塊(512字節)
  • c —— 字節。
  • w —— 字(2字節)
  • k —— 1024字節
  • M —— 1024K字節
  • G —— 1024M字節
$ find . -type f -name "*.swp" -delete    #刪除匹配的文件:
$ find . -type f -perm 644 -print       # 打印出權限為644的文件
$ find . -type f -user fzk -print       #打印出用戶fzk擁有的文件
# find . -type f -user root -exec chown fzk {} \;
#找到root用戶的文件,執行chown fzk {},這里{}會替換成相應的文件。如果找到1.txt,2.txt。
它會被解析為 chown fzk test1.txt 和 chown fzk test2.txt。相當於兩條命令
想在一條命令中修改一個列表的文件,在exec中使用+來代替
$ find . -type f -name "*.c" -exec cat {} \;>all_c_files.txt #找到所有c文件,寫入到單一文件

跳過指定的目錄:

$ find devel/source_path \( -name ".git" -prune \) -o \( -type f -print \)

以上命令打印出不包括在.git目錄中的所有文件的名稱(路徑)。\( -name ".git" -prune \) 的作用是用於進行排除,它指明了 .git目錄應該被排除在外,而 \( -type f -print \) 指明了需要執行的動作。這些動作需要被放置在第二個語句塊中(打印出所有文件的名稱和路徑)。

xargs:

xargs 能夠處理 stdin 並將其轉換為特定命令的命令行參數。 xargs 也可以將單行或多行文本輸入轉換成其他格式,例如單行變多行或是多行變單行。

$ cat example.txt | xargs          #多行變單行
1 2 3 4 5 6 $ echo
1 2 3 4 5 6 | xargs -n 3 #單行變多行
1 2 3
4 5 6
$ echo "splitXsplitXsplitXsplit" | xargs -d X -n 2
split split
split split
$ find . -type f -name "*.txt" -print0 | xargs -0 rm -f

找到所有.txt刪掉。如果用這條命令 find . -type f -name "*.txt" -print | xargs rm -f 文件中的空格會當做文件的分隔符,最后不知道都會刪掉什么,-print0 已字符null('\0')來分隔輸出,xargs -0 將\0 作為輸入定界符。

tr:tr 可以對來自標准輸入的內容進行字符替換、字符刪除以及重復字符壓縮。它可以將一組字符變成另一組字符,因而通常也被稱為轉換(translate)命令。

$ echo "HELLO WHO IS THIS" | tr 'A-Z' 'a-z'    
hello who is this
$ echo "Hello 123 world 456" | tr -d '0-9'   #-d 刪除字符
Hello world
$ echo hello 1 char 2 next 4 | tr -d -c '0-9 \n' #-c 補集,刪掉除了0-9 空格 換行符的其他字符,也就是只留下這些字符
1 2 4
$ echo "GNU   is       not UNIX. Recursive     right ?  | tr -s ' ' #刪掉重復的‘ ’
GNU is not UNIX. Recursive right ?

排序:sort

$ sort file1.txt file2.txt > sorted.txt
$ sort -n file.txt                                #按照數字進行排序
$ sort -r file.txt                                #按照逆序進行排序
$ sort -M months.txt                       #按照月份進行排序(依照一月,二月,三月......)
$ sort -m sorted1 sorted2              #合並兩個已排序過的文件
$ sort file1.txt file2.txt | uniq           #找出已排序文件中不重復的行

臨時文件:

$ filename=`mktemp`   #創建了一個臨時文件,並打印出存儲在 $filename 中的文件名。
$ echo $filename
/tmp/tmp.8xvhkjF5fH
$ dirname=`mktemp -d` #創建了一個臨時目錄,並打印出存儲在 $dirname 中的目錄名。
$ echo $dirname
tmp.NI8xzW7VRX
$ tmpfile=`mktemp -u` #文件名被存儲在 $tmpfile 中,但並沒有創建對應的文件。
$ echo $tmpfile
/tmp/tmp.RsGmilRpcT
$mktemp test.XXX      #根據模板創建臨時文件名
test.2tc

分隔文件和數據:

$ split -b 10k data.file -d -a 4 x
$ ls
data.file x0009 x0019 x0029 x0039 x0049 x0059 x0069 x0079

上面的代碼將data.file分隔成每個文件大小是10kb的若干個文件。-d是以數字為后綴(沒有這個參數是字母),10k還可以用M、G、c(byte)、w(word)、l(line 行數)等。-a length指定后綴的長度。最后一個參數PREFIX,前綴,是什么字符前綴就是什么。

假如分隔一個日志文件server.log,將日志分隔成server1.log, server2.log, server3.log

$ cat server.log

SERVER-1
[connection] 192.168.0.1
[connection] 192.168.0.2
[disconnect] 192.168.0.3
[connection] 192.168.0.4
SERVER-2
[connection] 192.168.0.1
[connection] 192.168.0.2
[disconnect] 192.168.0.3
[connection] 192.168.0.4
SERVER-3
[connection] 192.168.0.1
[connection] 192.168.0.2
[disconnect] 192.168.0.3
[connection] 192.168.0.4

$ csplit server.log /SERVER/ -n 2 -s {*} -f server -b "%02d.log" ; rm server00.log
$ ls
server01.log server02.log server03.log server.log

csplit 是 split 工具的一個變體。 split 只能夠根據數據大小或行數分割文件,而 csplit可以根據文本自身的特點進行分割。

  • /SERVER/ 用來匹配某一行。/[REGEX]/ 表示文本樣式。包括從當前行(第一行)直到(但不包括)包含“ SERVER ”的匹配行。
  • {*} 表示根據匹配重復執行分割,直到文件末尾為止。可以用{整數}的形式來指定分割執行的次數。

  • -s 使命令進入靜默模式,不打印其他信息。
  • -n 指定分割后的文件名后綴的數字個數,例如01、02、03等
  • -f 指定分割后的文件名前綴(在上面的例子中,server就是前綴)。

  • -b 指定后綴格式。例如 %02d.log ,類似於C語言中 printf 的參數格式。在這里文件名=前綴+后綴= server + %02d.log 。

  • 因為分割后的第一個文件沒有任何內容(匹配的單詞就位於文件的第一行中),所以我們刪除了server00.log。

分隔字符串:

$ file_jpg="sample.jpg"
$ name=${file_jpg%.*}
$ echo File name is: $name
File name is: sample

${VAR%.*} 的含義如下所述:
從 $VAR 中刪除位於 % 右側的通配符(在前例中是. * )所匹配的字符串。通配符從右向左進行匹配。
給 VAR 賦值, VAR=sample.jpg 。那么,通配符從右向左就會匹配到.jpg,因此,從 $VAR中刪除匹配結果,就會得到輸出 sample

% 屬於非貪婪(non-greedy)操作。它從右到左找出匹配通配符的最短結果。還有另一個操作符 %% ,這個操作符與 % 相似,但行為模式卻是貪婪的,這意味着它會匹配符合條件的最長的字符串。

$VAR=hack.fun.book.txt
$ echo ${VAR%.*}
hack.fun.book
$ echo ${VAR%%.*}
hack

還有一個:

$ file_jpg="sample.jpg"
$ extension=${file_jpg#*.}
$ echo Extension is: $extension
Extension is: jpg

${VAR#*.} 的含義如下所述:
從 $VAR 中刪除位於 # 右側的通配符(即在前例中使用的 *. )所匹配的字符串。通配符從左向右進行匹配。## 從左向右進行貪婪匹配,並從指定變量中刪除匹配結果。
可能發現了,都是刪除匹配的為什么還要有兩種匹配方式,用${file_jpg%*.}不能代替${file_jpg#*.}嗎?看下面

http="www.sample.com"
echo ${http%.com}
echo ${http%.co}
echo ${http%.*}

echo ${http#www.}
echo ${http#ww.}
echo ${http#*.}
結果:
www.sample
www.sample.com
www.sample
sample.com
www.sample.com
sample.com

可以看出%只能從最右側開始匹配,#只能從最左側開始匹配。

批量重命名移動的例子:運用find和上面的分隔字符串的方法

count=1;
for img in `find . -iname '*.png' -o -iname '*.jpg' -type f -maxdepth 1`
do
    new=image-$count.${img##*.}
    echo "Renaming $img to $new"
    mv "$img" "$new"
    let count++
done

實現自動交互輸入。

首先有一個運行起來需要你輸入參數的腳本interactive.sh

#!/bin/bash
read -p "Enter param1 :" param1;
read -p "Enter param2 :" param2;
echo "you entered : $param1 $param2"

運行前手動輸入$ echo -e "1\nhello\n" | ./interactive.sh 和 從文本讀取 ./interactive.sh < input.data 都可以實現。
還有一種利用expect來實現。一般的系統中不帶expect包,需要自己下載,我的系統是ubuntu14.04 ,在下載expect前需要下載tcl。ubuntu系統直接運行:

sudo apt-get install tcl tk expect

裝好了后,建一個expect.sh腳本

#!/usr/bin/expect
spawn ./interactice.sh
expect "Enter param1 :"
send "1\n"
expect "Enter param2 :"
send "2\n"
expect eof
輸出:
spawn ./interactive.sh
Enter param1 :1
Enter param2 :2
you entered : 1 2
 
        

spwan:指定那個命令需要自動化。expect:提供需要等待的消息。send:等待的消息到達后向終端發送消息。expect eof:命令交互結束。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM