The Missing Semester of Your CS Education(計算機教育中缺失的一課)

Shell 工具和腳本(Shell Tools and Scripting)


一、shell腳本

1.1、變量賦值

在bash中為變量賦值的語法是foo=bar,訪問變量中存儲的數值,其語法為?$foo。 需要注意的是,foo = bar?(使用空格隔開)是不能正確工作的,因為解釋器會調用程序foo?并將?=?和?bar作為參數。 總的來說,在shell腳本中使用空格會起到分割參數的作用,有時候可能會造成混淆,請務必多加檢查。?

Bash中的字符串通過'?和?"分隔符來定義,但是它們的含義并不相同。以'定義的字符串為原義字符串,其中的變量不會被轉義,而?"定義的字符串會將變量值進行替換。

和其他大多數的編程語言一樣,bash也支持if,?case,?while?和?for?這些控制流關鍵字。同樣地,?bash?也支持函數,它可以接受參數并基于參數進行操作。下面這個函數是一個例子,它會創建一個文件夾并使用cd進入該文件夾。

在這里我們首先調用了mcd()函數,之后調用"mkdir"命令來創建這個目錄,"$1"是一種特殊變量,類似于其他語言里的"argv","argv"第一項將包含該參數。

"sourch mcd.sh"將會在我們的shell中加載并執行這個腳本。

"mcd test"我們從當前目錄移動到"test"目錄,

這里?$1?是腳本的第一個參數。與其他腳本語言不同的是,bash使用了很多特殊的變量來表示參數、錯誤代碼和相關變量。下面列舉了其中一些變量,更完整的列表可以參考?這里。

  • $0?- 腳本名
  • $1?到?$9?- 腳本的參數。?$1?是第一個參數,依此類推。
  • $@?- 所有參數
  • $#?- 參數個數
  • $??- 前一個命令的返回值
  • $$?- 當前腳本的進程識別碼
  • !!?- 完整的上一條命令,包括參數。常見應用:當你因為權限不足執行命令失敗時,可以使用?sudo !!再嘗試一次。
  • $_?- 上一條命令的最后一個參數。如果你正在使用的是交互式 shell,你可以通過按下?Esc?之后鍵入 . 來獲取這個值。

上圖,"echo $?"返回0則代表程序正常執行返回,沒有問題,而返回1則代表程序出現錯誤,"grep foorbar mcd.sh":表示從"mcd.sh"中尋找"foobar"字符串,但是該字符串不存在,故,"grep"不會輸出任何內容,但是會返回1告訴我們程序發生了錯誤。

命令通常使用?STDOUT來返回輸出值,使用STDERR?來返回錯誤及錯誤碼,便于腳本以更加友好的方式報告錯誤。 返回碼或退出狀態是腳本/命令之間交流執行狀態的方式。返回值0表示正常執行,其他所有非0的返回值都表示有錯誤發生。

退出碼可以搭配?&&(與操作符 - 在第一個命令沒有錯誤的情況下執行第二個命令)和?||(或操作符 - 第一個命令失敗,則執行第二個命令,第一個命令成功,則跳過第二個命令(短路))使用,用來進行條件判斷,決定是否執行其他程序。它們都屬于短路運算符(short-circuiting)。同一行的多個命令可以用?;?分隔。程序?true?的返回碼永遠是0false?的返回碼永遠是1

"foo=$(pwd)":該命令打印我們現在所在的當前工作目錄,然后將其存儲到"foo"變量中。

我們上述的命令都是將變量擴展為字符串輸出,我們還有另一個"魔法"叫進程替換,它和命令替換有些相似,"cat < (ls) < (ls ..)":命令會在內部執行,然后將輸出放入一個類似臨時文件的東西中,并將文件標識符提供給最左邊的命令。所以在這種情況下,我們正在查看這個目錄,將其放入臨時文件中,并對父文件夾做同樣的事情,然后將兩個文件連接起來。(因為一些命令并不是從標準輸入流中獲取輸入的,而是從某些文件中獲取的)

所以我們得到了兩個命令連在一起的輸出。

#!/bin/bashecho "Starting program at $(date)" # date會被替換成日期和時間echo "Running program $0 with $# arguments with pid $$"for file in "$@"; dogrep foobar "$file" > /dev/null 2> /dev/null# 如果模式沒有找到,則grep退出狀態為 1# 我們將標準輸出流和標準錯誤流重定向到Null,因為我們并不關心這些信息if [[ $? -ne 0 ]]; thenecho "File $file does not have any foobar, adding one"echo "# foobar" >> "$file"fi
done

"$0":我們正在運行的腳本文件名。

"$#":我們給該命令參數的數量。

"$$":正在運行該命令的進程ID(PID)

"$@":它將展開為所有的參數,因此我們可以不必限定參數而只編寫"$1"、"$2"、"$3",如果我們不知道有多少參數,可以將所有的參數放在那里。然后將其傳遞給for循環,for循環將依次創建"file"變量,并將每一個參數賦值給"file"。

"grep":在某個文件中搜索字符串

如果我們關心程序的輸出,我們可以將其重定向到某個地方,以保存它或是連接其它文件。但在這里,我們需要關注錯誤代碼,對于這個腳本,我們想知道"grep"是否運行成功。我們完全可以忽略輸出,則將輸出重定向到"/dev/null"(黑洞文件),它是UNIX中的一種特殊設備,你可以像寫入文件一樣寫入數據,但它將被丟棄,"2>"用于重定向標準錯誤流,因為這兩個流是分開的,你需要告訴Bash如何去處理它。所以在這行代碼中,我們檢查文件是否包含"foobar",如果包含,則返回0;如果不包含,則返回非0的錯誤代碼。

在條件語句中,我們比較?$??是否等于0。"-ne":"not equal"(不相等)

"echo # foobar":希望這是對文件的注釋,然后我們使用">>"在文件末尾追加,因為文件名已經被傳遞給腳本,我們之前不知道文件名,所以我們用了"file"變量,現在要將其展開。

Bash實現了許多類似的比較操作,您可以查看?test 手冊(或于終端上鍵入命令"man test")。 在bash中進行比較時,盡量使用雙方括號?[[ ]]?而不是單方括號?[ ],這樣會降低犯錯的幾率,盡管這樣并不能兼容?sh。 更詳細的說明參見這里。

下面的圖片我們使用"ls"可以看到現在路徑下的文件,如果我們想要顯示所有后綴是".sh"的文件該怎么樣呢?我們可以使用"*",即在想要顯示的文件后綴前加入它,就可以完成要求。?

上圖我們可以看到我們擁有以"project"開頭的文件,假設我們現在只想匹配"project"后加一個字符,而并非兩個字符,我們可以使用"?",它可以擴展為任何一個字符。

當執行腳本時,我們經常需要提供形式類似的參數。bash使我們可以輕松的實現這一操作,它可以基于文件擴展名展開表達式。這一技術被稱為shell的?通配globbing

  • 通配符 - 當你想要利用通配符進行匹配時,你可以分別使用???和?*?來匹配一個或任意個字符。例如,對于文件foo,?foo1,?foo2,?foo10?和?bar,?rm foo?這條命令會刪除foo1?和?foo2?,而rm foo*?則會刪除除了bar之外的所有文件。
  • 花括號{}?- 當你有一系列的指令,其中包含一段公共子串時,可以用花括號來自動展開這些命令。這在批量移動或轉換文件時非常方便。
convert image.{png,jpg}
# 會展開為
convert image.png image.jpgcp /path/to/project/{foo,bar,baz}.sh /newpath
# 會展開為
cp /path/to/project/foo.sh /path/to/project/bar.sh /path/to/project/baz.sh /newpath# 也可以結合通配使用
mv *{.py,.sh} folder
# 會移動所有 *.py 和 *.sh 文件mkdir foo bar# 下面命令會創建foo/a, foo/b, ... foo/h, bar/a, bar/b, ... bar/h這些文件
touch {foo,bar}/{a..h}
touch foo/x bar/y
# 比較文件夾 foo 和 bar 中包含文件的不同
diff <(ls foo) <(ls bar)
# 輸出
# < x
# ---
# > y

假設我們在文件夾中有一個圖像,我們想將其從PNG轉化成JPG,?我們只需輸入"convert image.{png,jpg}"即可,我們還可以輸入"touch foo{}"touch一串foo,所有這些都將會被擴展。

我們還可多層操作,將它們做笛卡爾積,"touch project{1,2}/src/test/test{1,2,3}.py"如果我們有一些這樣的組:"{1,2}",然后后面有{1,2,3},這將以這兩個組的笛卡爾積進行擴展,并將擴展為下圖所有的這些東西,我們可以快速的創建文件。

我們還可以將"*"和"{}"結合使用,我們還可以用類似區間的東西。

這將會擴展到"foo/a"、"foo/b"...所有這些組合,一直到"j",同樣,對于"bar"也是如此。

那么如果我們在這兩個目錄創建兩個不同的文件,并且演示一下之前講過的進程替換,比如我們想檢查一下這兩個文件夾有哪些個文件不同,我們可以使用"diff",并讓其在兩個"ls"的輸出間作比較。

毫無疑問,我們得到的結果是"x"存在于第一個文件夾中,"y"存在于第二個文件夾中。?

編寫?bash?腳本有時候會很別扭和反直覺。例如?shellcheck?這樣的工具可以幫助你定位sh/bash腳本中的錯誤。

注意,腳本并不一定只有用 bash 寫才能在終端里調用。比如說,這是一段 Python 腳本,作用是將輸入的參數倒序輸出:

#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):print(arg)

內核知道去用 python 解釋器而不是 shell 命令來運行這段腳本,是因為腳本的開頭第一行的?shebang。

在?shebang?行中使用?env?命令是一種好的實踐,它會利用環境變量中的程序來解析該腳本,這樣就提高了您的腳本的可移植性。env?會利用我們第一節講座中介紹過的PATH?環境變量來進行定位。 例如,使用了env的shebang看上去是這樣的#!/usr/bin/env python

shell函數和腳本有如下一些不同點:

  • 函數只能與shell使用相同的語言,腳本可以使用任意語言。因此在腳本中包含?shebang?是很重要的。
  • 函數僅在定義時被加載,腳本會在每次被執行時加載。這讓函數的加載比腳本略快一些,但每次修改函數定義,都要重新加載一次。
  • 函數會在當前的shell環境中執行,腳本會在單獨的進程中執行。因此,函數可以對環境變量進行更改,比如改變當前工作目錄,腳本則不行。腳本需要使用?export?將環境變量導出,并將值傳遞給環境變量。
  • 與其他程序語言一樣,函數可以提高代碼模塊性、代碼復用性并創建清晰性的結構。shell腳本中往往也會包含它們自己的函數定義。

"shellcheck"工具:它可以為我們提供警告、語法錯誤,和其他可能不正確引用或文件中錯放空格的問題。例如,對于非常簡單的"mcd.sh"文件,我們會得到一些錯誤提示,我們缺少了"shebang",如果在不同的系統上運行,可能無法正確解釋它;另外,"cd"的命令可能會不正確的擴展,我們可以使用它下面給出的方法。這樣做更改的效果是,如果"cd"沒有正確執行,你不能"cd"進入該文件夾,因為要么你沒有權限,要么其不存在,這將返回一個非0的錯誤代碼,腳本將執行"exit",這將停止腳本的運行,而不是繼續對實際不存在的地方執行命令。

二、Shell工具

2.1、查看命令如何使用

看到這里,您可能會有疑問,我們應該如何為特定的命令找到合適的標記呢?例如?ls -l,?mv -i?和?mkdir -p。更普遍的是,給您一個命令行,您應該怎樣了解如何使用這個命令行并找出它的不同的選項呢? 一般來說,您可能會先去網上搜索答案,但是,UNIX 可比 StackOverflow 出現的早,因此我們的系統里其實早就包含了可以獲取相關信息的方法。

在上一節中我們介紹過,最常用的方法是為對應的命令行添加-h?或?--help?標記。另外一個更詳細的方法則是使用man?命令。man?命令是手冊(manual)的縮寫,它提供了命令的用戶手冊。

例如,man rm?會輸出命令?rm?的說明,同時還有其標記列表,包括之前我們介紹過的-i。 事實上,目前我們給出的所有命令的說明鏈接,都是網頁版的Linux命令手冊。即使是您安裝的第三方命令,前提是開發者編寫了手冊并將其包含在了安裝包中。在交互式的、基于字符處理的終端窗口中,一般也可以通過?:help?命令或鍵入???來獲取幫助。

有時候手冊內容太過詳實,讓我們難以在其中查找哪些最常用的標記和語法。?TLDR pages?是一個很不錯的替代品,它提供了一些案例,可以幫助您快速找到正確的選項。

例如,自己就常常在tldr上搜索tar?和?ffmpeg?的用法。

2.2、查找文件

如果我們已經知道要查找所有名為"src"的文件,那么我們可以用"find",我們將在當前文件夾下調用"find","."表示當前文件夾,我們要讓它的名稱為"src",類型是文件夾,輸入這個命令后,它將遞歸的瀏覽文件夾,并查找所有符合該標準的文件或文件夾。

我們還可以用它來查找特定模式的文件路徑,我們希望有一些文件夾(并不在意數量),我們想要的是所有的Python腳本,所有擴展名為".py"的東西,這些都需要在名為test的文件夾下。"-type f"代表查找類型是文件。

你還可以查找修改過的文件,"-mtime"用于修改時間,在過去一天內修改過的文件,這幾乎將是所有文件。

"find"不僅僅可以用于查找,我們還可以在找到這些文件時進行一些操作。所有,我們查找所有擴展名為"tmp"的文件,并且我們告訴"find",在此文件中的所有文件我們將對其執行"rm"命令。

# 刪除全部擴展名為.tmp 的文件
find . -name '*.tmp' -exec rm {} \;
# 查找全部的 PNG 文件并將其轉換為 JPG
find . -name '*.png' -exec convert {} {}.jpg \;

如果我們只想匹配以"tmp"結尾的內容,你可以使用"fd",這是一個更短的命令,它默認使用正則表達式,甚至還能忽略搜索你的git文件。

# 查找所有名稱為src的文件夾
find . -name src -type d
# 查找所有文件夾路徑中包含test的python文件
find . -path '*/test/*.py' -type f
# 查找前一天修改的所有文件
find . -mtime -1
# 查找所有大小在500k至10M的tar.gz文件
find . -size +500k -size -10M -name '*.tar.gz'

fd?就是一個更簡單、更快速、更友好的程序,它可以用來作為find的替代品。它有很多不錯的默認設置,例如輸出著色、默認支持正則匹配、支持unicode并且我認為它的語法更符合直覺。以模式PATTERN?搜索的語法是?fd PATTERN

大多數人都認為?find?和?fd?已經很好用了,但是有的人可能想知道,我們是不是可以有更高效的方法,例如不要每次都搜索文件而是通過編譯索引或建立數據庫的方式來實現更加快速地搜索。

這就要靠?locate?了。?locate?使用一個由?updatedb負責更新的數據庫,在大多數系統中?updatedb?都會通過?cron?(Cron是一種在Unix、Linux和類Unix操作系統中用于定期運行任務的工具。它允許用戶指定一個或多個命令或腳本,并設置在指定時間或周期運行這些命令或腳本的方式。)每日更新。這便需要我們在速度和時效性之間作出權衡。而且,find?和類似的工具可以通過別的屬性比如文件大小、修改時間或是權限來查找文件,locate則只能通過文件名。?這里有一個更詳細的對比。

"locate":只會查找你的文件系統中包含你想要的子字符串的路徑。該命令找路徑的速度很快,因為它已經在此上構建了一個系統,為了使其保持更新,我們使用"updatedb"命令來更新數據庫。

如果我們想查找文件的內容,可以使用之前提到的"grep"命令。

如果你想要在再次遞歸的搜索當前結構,并查找更多文件,怎么辦?我們可以使用"find"和"-exec"。但其實,"grep"有"-R"標志,它將遍歷整個目錄。

2.3、查找代碼

查找文件是很有用的技能,但是很多時候您的目標其實是查看文件的內容。一個最常見的場景是您希望查找具有某種模式的全部文件,并找它們的位置。

為了實現這一點,很多類UNIX的系統都提供了grep命令,它是用于對輸入文本進行匹配的通用工具。它是一個非常重要的shell工具,我們會在后續的數據清理課程中深入的探討它。

grep?有很多選項,這也使它成為一個非常全能的工具。其中我經常使用的有?-C?:獲取查找結果的上下文(Context);-v?將對結果進行反選(Invert),也就是輸出不匹配的結果。舉例來說,?grep -C 5?會輸出匹配結果前后五行。當需要搜索大量文件的時候,使用?-R?會遞歸地進入子目錄并搜索所有的文本文件。

但是,我們有很多辦法可以對?grep -R?進行改進,例如使其忽略.git?文件夾,使用多CPU等等。

因此也出現了很多它的替代品,包括?ack,?ag?和?rg。它們都特別好用,但是功能也都差不多,我比較常用的是 ripgrep (rg) ,因為它速度快,而且用法非常符合直覺。例子如下:

rg "import requests" -t py ~/scratch # 在"scratch"文件中快速搜索導入了"requests"庫
的所有的python文件# 查找所有使用了 requests 庫的文件
rg -t py 'import requests'# 查找所有沒有寫 shebang("^#!" - 正則表達式) 的文件(包含隱藏文件 -u不忽略隱藏文件)
rg -u --files-without-match "^#!"# 查找所有的foo字符串,并打印其之后的5行
rg foo -A 5# 打印匹配的統計信息(匹配的行和文件的數量)
rg --stats PATTERN
rg "import requests" -t py -C 5 --stats ~/scratch # 它會告訴我們搜索所有內容的信息

2.4、查找shell命令

history?命令允許您以程序員的方式來訪問shell中輸入的歷史命令。這個命令會在標準輸出中打印shell中的歷史命令。如果我們要搜索歷史記錄,則可以利用管道將輸出結果傳遞給?grep?進行模式搜索。?history | grep find?會打印包含find子串的命令。

對于大多數的shell來說,您可以使用?Ctrl+R?對命令歷史記錄進行回溯搜索。敲?Ctrl+R?后您可以輸入子串來進行匹配,查找歷史命令行。?

Ctrl+R?可以配合?fzf?使用。fzf?是一個通用的模糊查找工具,它可以和很多命令一起使用。這里我們可以對歷史命令進行模糊查找并將結果以賞心悅目的格式輸出。

"歷史子字符串搜索":另外一個和歷史命令相關的技巧我喜歡稱之為基于歷史的自動補全。 這一特性最初是由?fish?shell 創建的,它可以根據您最近使用過的開頭相同的命令,動態地對當前的shell命令進行補全。這一功能在?zsh?中也可以使用,它可以極大的提高用戶體驗。?

2.5、文件夾導航

"-R"來遞歸的列出某個目錄的結構。

"tree":更友好的打印目錄結構。?

之前對所有操作我們都默認一個前提,即您已經位于想要執行命令的目錄下,但是如何才能高效地在目錄間隨意切換呢?有很多簡便的方法可以做到,比如設置alias,使用?ln -s?創建符號連接等。而開發者們已經想到了很多更為精妙的解決方案。

由于本課程的目的是盡可能對你的日常習慣進行優化。因此,我們可以使用fasd和?autojump?這兩個工具來查找最常用或最近使用的文件和目錄。

Fasd 基于?frecency?對文件和文件排序,也就是說它會同時針對頻率(frequency)和時效(recency)進行排序。默認情況下,fasd使用命令?z?幫助我們快速切換到最常訪問的目錄。例如, 如果您經常訪問/home/user/files/cool_project?目錄,那么可以直接使用?z cool?跳轉到該目錄。對于 autojump,則使用j cool代替即可。

還有一些更復雜的工具可以用來概覽目錄結構,例如?tree,?broot?或更加完整的文件管理器,例如?nnn?或?ranger。

三、練習

  1. Read?man ls?and write an?ls?command that lists files in the following manner

    • Includes all files, including hidden files
    • Sizes are listed in human readable format (e.g. 454M instead of 454279954)
    • Files are ordered by recency
    • Output is colorized

    A sample output would look like this

     -rw-r--r--   1 user group 1.1M Jan 14 09:53 bazdrwxr-xr-x   5 user group  160 Jan 14 09:53 .-rw-r--r--   1 user group  514 Jan 14 06:42 bar-rw-r--r--   1 user group 106M Jan 13 12:12 foodrwx------+ 47 user group 1.5K Jan 12 18:08 ..
    
  2. Write bash functions?marco?and?polo?that do the following. Whenever you execute?marco?the current working directory should be saved in some manner, then when you execute?polo, no matter what directory you are in,?polo?should?cd?you back to the directory where you executed?marco. For ease of debugging you can write the code in a file?marco.sh?and (re)load the definitions to your shell by executing?source marco.sh.

  3. Say you have a command that fails rarely. In order to debug it you need to capture its output but it can be time consuming to get a failure run. Write a bash script that runs the following script until it fails and captures its standard output and error streams to files and prints everything at the end. Bonus points if you can also report how many runs it took for the script to fail.

     #!/usr/bin/env bashn=$(( RANDOM % 100 ))if [[ n -eq 42 ]]; thenecho "Something went wrong">&2 echo "The error was using magic numbers"exit 1fiecho "Everything went according to plan"
    
  4. As we covered in the lecture?find’s?-exec?can be very powerful for performing operations over the files we are searching for. However, what if we want to do something with?all?the files, like creating a zip file? As you have seen so far commands will take input from both arguments and STDIN. When piping commands, we are connecting STDOUT to STDIN, but some commands like?tar?take inputs from arguments. To bridge this disconnect there’s the?xargs?command which will execute a command using STDIN as arguments. For example?ls | xargs rm?will delete the files in the current directory.

    Your task is to write a command that recursively finds all HTML files in the folder and makes a zip with them. Note that your command should work even if the files have spaces (hint: check?-d?flag for?xargs).

    If you’re on macOS, note that the default BSD?find?is different from the one included in?GNU coreutils. You can use?-print0?on?find?and the?-0?flag on?xargs. As a macOS user, you should be aware that command-line utilities shipped with macOS may differ from the GNU counterparts; you can install the GNU versions if you like by?using brew.

  5. (Advanced) Write a command or script to recursively find the most recently modified file in a directory. More generally, can you list all files by recency?

習題解答

  1. 閱讀?man ls?,然后使用ls?命令進行如下操作:

    • 所有文件(包括隱藏文件)
    • 文件打印以人類可以理解的格式輸出 (例如,使用454M 而不是 454279954)
    • 文件以最近訪問順序排序
    • 以彩色文本顯示輸出結果

    典型輸出如下:

     -rw-r--r--   1 user group 1.1M Jan 14 09:53 bazdrwxr-xr-x   5 user group  160 Jan 14 09:53 .-rw-r--r--   1 user group  514 Jan 14 06:42 bar-rw-r--r--   1 user group 106M Jan 13 12:12 foodrwx------+ 47 user group 1.5K Jan 12 18:08 ..
    
  2. 編寫兩個bash函數?marco?和?polo?執行下面的操作。 每當你執行?marco?時,當前的工作目錄應當以某種形式保存,當執行?polo?時,無論現在處在什么目錄下,都應當?cd?回到當時執行?marco?的目錄。 為了方便debug,你可以把代碼寫在單獨的文件?marco.sh?中,并通過?source marco.sh命令,(重新)加載函數。

  3. 假設您有一個命令,它很少出錯。因此為了在出錯時能夠對其進行調試,需要花費大量的時間重現錯誤并捕獲輸出。 編寫一段bash腳本,運行如下的腳本直到它出錯,將它的標準輸出和標準錯誤流記錄到文件,并在最后輸出所有內容。 加分項:報告腳本在失敗前共運行了多少次。

     #!/usr/bin/env bashn=$(( RANDOM % 100 ))if [[ n -eq 42 ]]; thenecho "Something went wrong">&2 echo "The error was using magic numbers"exit 1fiecho "Everything went according to plan"
    
  4. 本節課我們講解的?find?命令中的?-exec?參數非常強大,它可以對我們查找的文件進行操作。但是,如果我們要對所有文件進行操作呢?例如創建一個zip壓縮文件?我們已經知道,命令行可以從參數或標準輸入接受輸入。在用管道連接命令時,我們將標準輸出和標準輸入連接起來,但是有些命令,例如tar?則需要從參數接受輸入。這里我們可以使用xargs?命令,它可以使用標準輸入中的內容作為參數。 例如?ls | xargs rm?會刪除當前目錄中的所有文件。

    您的任務是編寫一個命令,它可以遞歸地查找文件夾中所有的HTML文件,并將它們壓縮成zip文件。注意,即使文件名中包含空格,您的命令也應該能夠正確執行(提示:查看?xargs的參數-d,譯注:MacOS 上的?xargs沒有-d,查看這個issue)

    如果您使用的是 MacOS,請注意默認的 BSD?find?與?GNU coreutils?中的是不一樣的。你可以為find添加-print0選項,并為xargs添加-0選項。作為 Mac 用戶,您需要注意 mac 系統自帶的命令行工具和 GNU 中對應的工具是有區別的;如果你想使用 GNU 版本的工具,也可以使用?brew 來安裝。

  5. (進階)編寫一個命令或腳本遞歸的查找文件夾中最近使用的文件。更通用的做法,你可以按照最近的使用時間列出文件嗎?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/14582.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/14582.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/14582.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網工內推 | 香港移動,10年以上數通經驗,CCIE,5W-6W

01 香港移動招聘 &#x1f537;招聘崗位&#xff1a;網絡工程師 &#x1f537;崗位要求&#xff1a; 需要有10年及以上數通經驗&#xff0c;有CCIE 證書&#xff0c;懂技術管理&#xff0c;溝通暢通 &#x1f537;語言要求&#xff1a; 粵語英語 &#x1f537;薪資&#xff1…

基于灰狼優化算法優化RBF(GWO-RBF)的數據回歸預測(多輸入多輸出)

代碼原理及流程 基于灰狼優化算法優化多輸入多輸出&#xff08;MIMO&#xff09;的RBF神經網絡的數據回歸預測&#xff0c;可以采取以下步驟&#xff1a; 1. 數據準備&#xff1a;準備包含多個輸入特征和多個輸出目標的數據集&#xff0c;確保數據已經經過預處理和歸一化。 …

TCP - 半連接隊列和全連接隊列

目錄 一、半連接隊列和全連接隊列的概念 二、全連接隊列溢出 三、半連接隊列溢出 一、半連接隊列和全連接隊列的概念 1. 半連接隊列&#xff1a;服務端收到客戶端發送的 SYN 包時&#xff0c;內核會將該連接加入半連接 SYN 隊列&#xff0c;并向客戶端返回響應 2. 全連接隊…

CSS基礎(第二天)

Emmet語法 快速生成HTML結構語法 1. 生成標簽 直接輸入標簽名 按tab鍵即可 比如 div 然后tab 鍵&#xff0c; 就可以生成 <div></div> 2. 如果想要生成多個相同標簽 加上 * 就可以了 比如 div*3 就可以快速生成3個div 3. 如果有父子級關系的標簽&#xff0c;可以…

算法刷題筆記 數的范圍(C++實現)(二分法重要例題)

文章目錄 題目描述題目思路題目代碼&#xff08;C&#xff09;題目感想 題目描述 給定一個按照升序排列的長度為n的整數數組&#xff0c;以及q個查詢。對于每個查詢&#xff0c;返回一個元素k的起始位置和終止位置&#xff08;位置從0開始計數&#xff09;。如果數組中不存在該…

Docker【2】iptables 錯誤解決

iptables 錯誤解決 問題說明問題分析解決步驟1. 確保 iptables 模塊已加載2. 檢查和重啟 docker 服務3. 檢查 firewalld 狀態4. 重置 iptables 規則5. 查看和更新 Docker 配置 總結 問題說明 執行的 docker 命令如下&#xff0c;啟動 nginx 并設置宿主機端口 (8080) 與容器端口…

學習Uni-app開發小程序Day25

這一章學習了觸底加載更多阻止無效的網絡請求、分類列表存入Storage在預覽頁面讀取緩存展示、通過swiper的事件實現真正的壁紙預覽及切換 觸底加載更多阻止無效的網絡請求、load-more樣式的展現 前面已經學習了當列表觸底后&#xff0c;會繼續加載&#xff0c;當到最后一層后…

自動化測試--利用pytest實現整條業務鏈路測試

? 概述 前面一章講解了單個接口的測試&#xff0c;但是實際項目中&#xff0c;因為權限和登錄狀態的限制&#xff0c;大部分接口沒辦法直接訪問到&#xff0c;這時候我們想訪問到一個系統的接口&#xff0c;就需要模擬用戶登錄拿到用戶的token和所擁有的權限之后再將這些信息…

vivado2020.2創建hls仿真工程實現led閃爍

下載vivado2020.2后會有這個出現在桌面 點擊進入創建工程&#xff0c;這里注意不要有前面的\我再復制的時候復制錯了導致創建失敗 按f光標就會跳轉到下一個f開頭的函數處&#xff0c;要查找其他函數也同理 生成了一個synthesis summary文件 找到目錄下生成的.v文件 an 點…

Pod進階——資源限制以及探針檢查

目錄 一、資源限制 1、資源限制定義&#xff1a; 2、資源限制request和limit資源約束 3、Pod和容器的資源請求和限制 4、官方文檔示例 5、CPU資源單位 6、內存資源單位 7、資源限制實例 ①編寫yaml資源配置清單 ②釋放內存&#xff08;node節點&#xff0c;以node01為…

【知識蒸餾】多任務模型 logit-based 知識蒸餾實戰

一、什么是邏輯&#xff08;logit&#xff09;知識蒸餾 Feature-based蒸餾原理是知識蒸餾中的一種重要方法&#xff0c;其關鍵在于利用教師模型的隱藏層特征來指導學生模型的學習過程。這種蒸餾方式旨在使學生模型能夠學習到教師模型在特征提取和表示方面的能力&#xff0c;從…

有些錯誤,常犯常新、常新常犯:記錄一個使用element-plus的tooltip組件的錯誤

使用element-plus的tooltip組件&#xff0c;最開始的寫法是這樣的&#xff1a; <el-tooltipclass"box-item"effect"dark"content"tooltip content" ><el-button v-if"isDisabled" :underline"false" type"pr…

持續總結中!2024年面試必問 20 道 Redis面試題(五)

上一篇地址&#xff1a;持續總結中&#xff01;2024年面試必問 20 道 Redis面試題&#xff08;四&#xff09;-CSDN博客 九、Redis的同步機制了解么&#xff1f; Redis 的同步機制是其復制策略的核心部分&#xff0c;確保數據在主節點&#xff08;master&#xff09;和從節點…

【C語言】程序員自我修養之文件操作

【C語言】程序員自我修養之文件操作 &#x1f525;個人主頁&#xff1a;大白的編程日記 &#x1f525;專欄&#xff1a;C語言學習之路 文章目錄 【C語言】程序員自我修養之文件操作前言一.文件介紹1.1為什么使用文件1.2文件分類1.3二進制文件和文本文件 二.文件的打開和關閉2.…

桌面藏線大法

1有線改無線&#xff1a; 藍牙鼠標 藍牙鍵盤 藍牙耳機 2將排插貼到桌子底下 購物軟件上搜 3斷舍離 不要的電子產品統統扔掉 4 洞洞板和掛鉤 這個不用介紹了

爬蟲基本原理及requests庫用法

文章目錄 一、爬蟲基本原理1、什么是爬蟲2、爬蟲的分類3、網址的構成4、爬蟲的基本步驟5、動態【異步】頁面和靜態【同步】頁面6、請求頭 二、requests基本原理及使用1、chrome 抓包按鈕詳解1.1 Elements1.2 元素定位器1.3 Network1.4 All1.5 XHR1.6 Preserve log1.7 手機模式1…

暴雨信息液冷計算解決方案亮相CCIG 2024

5月24日&#xff0c;2024中國圖象圖形大會&#xff08;CCIG&#xff09;在陜西西安正式開幕。作為涵蓋圖像圖形各專業領域的綜合性的全國性學術會議&#xff0c;CCIG面向開放創新、交叉融合的發展趨勢&#xff0c;為圖像圖形相關領域的專家學者和產業界的同仁&#xff0c;搭建了…

Java+Spring+ MySQL + MyCat云HIS有哪些優勢?智慧醫療云(HIS)低成本與安全保障的完美結合

JavaSpring MySQL MyCat云HIS有哪些優勢&#xff1f;智慧醫療云(HIS)低成本與安全保障的完美結合 云HIS的優點包括節省成本、便捷高效、穩妥安全等。通過云HIS&#xff0c;醫療機構無需在本地建立機房、購買服務器和應用軟件&#xff0c;降低了硬件和人力成本。同時&#xff0…

虛擬化介紹

虛擬化介紹 概述概念特點優勢實現手段 虛擬化架構概述寄居虛擬化架構裸金屬虛擬化架構操作系統虛擬化架構混合虛擬化架構幾種虛擬化架構的比較虛擬化架構與虛擬化技術的關系 虛擬化技術分類服務器虛擬化技術分類 存儲虛擬化技術分類網絡虛擬化技術分類 服務器虛擬化技術處理器虛…

開源軟件 | 一文徹底搞懂許可證的定義、起源、分類及八大主流許可證,讓你選型不再頭疼

為什么開源軟件會存在許可證&#xff0c;許可證的起源與產生目的是為了解決什么問題&#xff1f;許可證的定義又是怎樣的&#xff1f;什么是Copyleft&#xff0c;與Copyright有何區別&#xff1f;開源軟件常見的許可證有哪些&#xff1f;這些許可證都有什么特點&#xff1f;接下…