簡體   English   中英

基本的 Linux Bash 腳本 while / for

[英]Basic Linux Bash Scripting while / for

在 bash 腳本中做我的第一步。 到目前為止,我已經完成了 5/7 的練習,卡在了最后兩個練習中,我找不到解決它的方法。

這是請求的練習的更好翻譯,同樣應該使用簡單的命令來解決,如 sed、echo、cat、uniq、tr、grep、wc 等:

編寫顯示默認 shell 是您機器上可用 shell 之一的用戶數量的命令行。 執行命令時,對於每個 shell,我們將得到與以下相同形式的結果:

  • 0 個用戶擁有 shell / bin / sh
  • 11 個用戶有 for shell/bin/bash
  • 0 個用戶有 for shell /usr/bin/sh
  • 0 個用戶有 for shell /usr/bin/bash

@托德

我今天早上做到了,你的 grep 重定向輸入給了我這個想法。 我是這樣做的:

  • result=$(grep -of <(echo $SHELL) /etc/passwd | uniq -c)
  • count=$(echo $result I awk -F” “ '{print $1}')
  • shell=$(echo $result | awk -F” “'{print $2}')
  • echo $result | while read i; do
  • echo $count utilisateurs ont pour shell $shell
  • done

Unix 實用程序和 Bash 進程替換

在 Bash 中,有很多方法可以做到這一點。 但是,從實際的角度來看,您實際上並不需要 Bash 本身來解決這個問題。 標准的 *nix 工具可以正常工作,前提是您正確鏈接它們。 例如,如果輸出的特定格式不是重點:

$ grep -of <(grep '^/.*$' /etc/shells) /etc/passwd | sort | uniq -c
   11 /bin/bash
    8 /bin/sh
    4 /usr/local/bin/fish

這基本上是從 /etc/shells 中 grep 你的 shell,然后 grep將該輸出視為一個文件來讀取表達式以匹配 /etc/passwd。 然后它將結果通過管道傳送到 sort 和 uniq 實用程序以計算出現次數。 它依賴於進程替換,並非所有 shell 都具有或以相同的方式表達,但是您的問題被標記為 Bash,因此避免 Bashisms 不是主要問題。

但是,由於並非所有 shell 都具有 Bash 所做的進程替換重定向,因此此類解決方案在不同 shell 之間並不是 100% 可移植的。 通常有一種類似的方法可以做到這一點,但有時跨系統和 shell 的可移植性確實很重要。 無論如何,這顯示了 grep、管道和進程替換的強大功能。

獲得原始數據后,您可以使用 printf、awk 甚至 sed 對輸出進行格式化(如果您希望它采用不同的格式)。 但是,如果重點是計算 shell 使用情況,那么在 Bash 或 zsh 中這是一種簡單的方法。

更便攜的選擇

更復雜(但也更便攜)的替代方法是使用 xargs 作為管道的核心元素。 例如:

$ grep -o '^/.*$' /etc/shells |
    xargs -I{} -L1 -- sh -c 'echo "{}"; grep -Fc "{}" /etc/passwd' |
    xargs -n2
/bin/bash 11
/bin/csh 0
/bin/ksh 0
/bin/sh 8
/bin/tcsh 0
/bin/zsh 0
/usr/local/bin/fish 4

這將找到 /etc/shells 中的所有 shell,一次一個地將它們傳遞給 Bourne 腳本以打印 shell 的路徑,並讓 grep 的固定字符串匹配器計算 /etc/passwd 中的出現次數,然后將輸出通過管道傳輸到 xargs再次加入線路,一次備份兩條。

這種方法的主要好處是它會列出所有shell,無論是否有人使用它們。 您甚至可以將最后一行更改為tac | xargs -n2 tac | xargs -n2如果您希望在外殼名稱之前列出數字。

這就是我如何理解您的問題,一種使用數組的方法,包括關聯數組。

#!/usr/bin/env bash

declare -A Shell_and_Users

while IFS=: read -ra lines; do
  login_shell="${lines[-1]}"
  ((Shell_and_Users["$login_shell"]++))
done < /etc/passwd

要檢查關聯數組Shell_and_Users的值, Shell_and_Users運行:

declare -p Shell_and_Users

現在循環遍歷關聯數組以打印所需的輸出。

for i in "${!Shell_and_Users[@]}"; do
  printf -- '- %d users have shell %s\n' "${Shell_and_Users[$i]}" "$i"
done

如果不是這種情況,那么編輯問題並添加更多詳細信息,如果文件/etc/shells應該是 parse/involve,因為上面的解決方案永遠不會打印0 users

你走在正確的軌道上。 您可以使用以下腳本:

#!/bin/bash

# usual syntax to read a file line by line
# https://linuxhint.com/while_read_line_bash/
while IFS= read -r shell
do
   # if our file contains commented lines, we ignore them
   [[ "$shell" =~ ^[[:space:]]*# ]] && continue

   count=$(grep -c ".*:$shell" /etc/passwd)
   echo "- $count users have shell $shell"
done < /etc/shells

這里, /etc/shells是一個包含系統上所有可用登錄 shell 的文件。 /etc/passwd是另一個系統文件,包含系統上每個用戶的列表,以及與之關聯的 shell(格式: <username>:XXX:XXX:XXX:XXX:XXX:<shell>

我們使用grep -c ".*:$shell" /etc/passwd來查找/etc/passwd中以:<the current shell>結尾的行數

編輯:如果教授為您提供了一個 shell 列表,您可以將它們全部粘貼到一個文件中(即input.txt )並將done < /etc/shells替換為done < ./input.txt

假設可以使用awk ,你可以試試這個

$ cat awk.script
#!/usr/bin/env awk -f

BEGIN {
    FS=":"
} $NF=="/bin/bash" {
    count_bash++
    bash=$NF
} $NF=="/usr/bin/bash" {
    count_bash2++
    bash2=$NF
} $NF=="/bin/sh" {
    count_sh++
    sh=$NF
} $NF=="/usr/bin/sh" {
    count_sh2++
    sh2=$NF
} END {
    print count_bash " users have a shell for "bash "\n" \
          count_bash2" users have a shell for "bash2 "\n" \
          count_sh " users have a shell for "sh "\n" \
          count_sh2" users have a shell for "sh2 "\n" \
}

這將提供與您預期的輸出類似的輸出,但是,如果未使用 shell,您將獲得一個空輸出,其中沒有值或路徑,這可能並不理想。

輸出

$ awk -f awk.script /etc/passwd
24 users have a shell for /bin/bash
 users have a shell for
2 users have a shell for /bin/sh
 users have a shell for

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM