mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-13 22:30:37 +08:00
Merge remote-tracking branch 'LCTT/master'
This commit is contained in:
commit
baa0f94f9b
77
sources/news/20191002 Fedora projects for Hacktoberfest.md
Normal file
77
sources/news/20191002 Fedora projects for Hacktoberfest.md
Normal file
@ -0,0 +1,77 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Fedora projects for Hacktoberfest)
|
||||
[#]: via: (https://fedoramagazine.org/fedora-projects-for-hacktoberfest/)
|
||||
[#]: author: (Ben Cotton https://fedoramagazine.org/author/bcotton/)
|
||||
|
||||
Fedora projects for Hacktoberfest
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
It’s October! That means its time for the annual [Hacktoberfest][2] presented by DigitalOcean and DEV. Hacktoberfest is a month-long event that encourages contributions to open source software projects. Participants who [register][3] and submit at least four pull requests to GitHub-hosted repositories during the month of October will receive a free t-shirt.
|
||||
|
||||
In a recent Fedora Magazine article, I listed some areas where would-be contributors could [get started contributing to Fedora][4]. In this article, I highlight some specific projects that provide an opportunity to help Fedora while you participate in Hacktoberfest.
|
||||
|
||||
### Fedora infrastructure
|
||||
|
||||
* [Bodhi][5] — When a package maintainer builds a new version of a software package to fix bugs or add new features, it doesn’t go out to users right away. First it spends time in the updates-testing repository where in can receive some real-world usage. Bodhi manages the flow of updates from the testing repository into the updates repository and provides a web interface for testers to provide feedback.
|
||||
* [the-new-hotness][6] — This project listens to [release-monitoring.org][7] (which is also on [GitHub][8]) and opens a Bugzilla issue when a new upstream release is published. This allows package maintainers to be quickly informed of new upstream releases.
|
||||
* [koschei][9] — koschei enables continuous integration for Fedora packages. It is software for running a service for scratch-rebuilding RPM packages in Koji instance when their build-dependencies change or after some time elapses.
|
||||
* [MirrorManager2][10] — Distributing Fedora packages to a global user base requires a lot of bandwidth. Just like developing Fedora, distributing Fedora is a collaborative effort. MirrorManager2 tracks the hundreds of public and private mirrors and routes each user to the “best” one.
|
||||
* [fedora-messaging][11] — Actions within the Fedora community—from source code commits to participating in IRC meetings to…lots of things—generate messages that can be used to perform automated tasks or send notifications. fedora-messaging is the tool set that makes sending and receiving these messages possible.
|
||||
* [fedocal][12] — When is that meeting? Which IRC channel was it in again? Fedocal is the calendar system used by teams in the Fedora community to coordinate meetings. Not only is it a good Hacktoberfest project, it’s also [looking for a new maintainer][13] to adopt it.
|
||||
|
||||
|
||||
|
||||
In addition to the projects above, the Fedora Infrastructure team has highlighted [good Hacktoberfest issues][14] across all of their GitHub projects.
|
||||
|
||||
### Community projects
|
||||
|
||||
* [bodhi-rs][15] — This project provides Rust bindings for Bodhi.
|
||||
* [koji-rs][16] — Koji is the system used to build Fedora packages. Koji-rs provides bindings for Rust applications.
|
||||
* [fedora-rs][17] — This project provides a Rust library for interacting with Fedora services like other languages like Python have.
|
||||
* [feedback-pipeline][18] — One of the current Fedora Council objectives is [minimization][19]: work to reduce the installation and patching footprint of Fedora releases. feedback-pipeline is a tool developed by this team to generate reports of RPM sizes and dependencies.
|
||||
|
||||
|
||||
|
||||
### And many more
|
||||
|
||||
The projects above are only a small sample focused on software used to build Fedora. Many Fedora packages have upstreams hosted on GitHub—too many to list here. The best place to start is with a project that’s important to you. Any contributions you make help improve the entire open source ecosystem. If you’re looking for something in particular, the [Join Special Interest Group][20] can help. Happy hacking!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/fedora-projects-for-hacktoberfest/
|
||||
|
||||
作者:[Ben Cotton][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/bcotton/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/09/hacktoberfest-816x345.jpg
|
||||
[2]: https://hacktoberfest.digitalocean.com/
|
||||
[3]: https://hacktoberfest.digitalocean.com/register
|
||||
[4]: https://fedoramagazine.org/how-to-contribute-to-fedora/
|
||||
[5]: https://github.com/fedora-infra/bodhi
|
||||
[6]: https://github.com/fedora-infra/the-new-hotness
|
||||
[7]: https://release-monitoring.org/
|
||||
[8]: https://github.com/release-monitoring/anitya
|
||||
[9]: https://github.com/fedora-infra/koschei
|
||||
[10]: https://github.com/fedora-infra/mirrormanager2
|
||||
[11]: https://github.com/fedora-infra/fedora-messaging
|
||||
[12]: https://github.com/fedora-infra/fedocal
|
||||
[13]: https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/message/GH4N3HYJ4ARFRP666O6EQCHDIQMXVUJB/
|
||||
[14]: https://github.com/orgs/fedora-infra/projects/4
|
||||
[15]: https://github.com/ironthree/bodhi-rs
|
||||
[16]: https://github.com/ironthree/koji-rs
|
||||
[17]: https://github.com/ironthree/fedora-rs
|
||||
[18]: https://github.com/minimization/feedback-pipeline
|
||||
[19]: https://docs.fedoraproject.org/en-US/minimization/
|
||||
[20]: https://fedoraproject.org/wiki/SIGs/Join
|
@ -1,5 +1,5 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
@ -1,340 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wenwensnow)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Hone advanced Bash skills by building Minesweeper)
|
||||
[#]: via: (https://opensource.com/article/19/9/advanced-bash-building-minesweeper)
|
||||
[#]: author: (Abhishek Tamrakar https://opensource.com/users/tamrakarhttps://opensource.com/users/dnearyhttps://opensource.com/users/sethhttps://opensource.com/users/sethhttps://opensource.com/users/marcobravo)
|
||||
|
||||
Hone advanced Bash skills by building Minesweeper
|
||||
======
|
||||
The nostalgia of classic games can be a great source for mastering
|
||||
programming. Deep dive into Bash with Minesweeper.
|
||||
![bash logo on green background][1]
|
||||
|
||||
I am no expert on teaching programming, but when I want to get better at something, I try to find a way to have fun with it. For example, when I wanted to get better at shell scripting, I decided to practice by programming a version of the [Minesweeper][2] game in Bash.
|
||||
|
||||
If you are an experienced Bash programmer and want to hone your skills while having fun, follow along to write your own version of Minesweeper in the terminal. The complete source code is found in this [GitHub repository][3].
|
||||
|
||||
### Getting ready
|
||||
|
||||
Before I started writing any code, I outlined the ingredients I needed to create my game:
|
||||
|
||||
1. Print a minefield
|
||||
2. Create the gameplay logic
|
||||
3. Create logic to determine the available minefield
|
||||
4. Keep count of available and discovered (extracted) mines
|
||||
5. Create the endgame logic
|
||||
|
||||
|
||||
### Print a minefield
|
||||
|
||||
In Minesweeper, the game world is a 2D array (columns and rows) of concealed cells. Each cell may or may not contain an explosive mine. The player's objective is to reveal cells that contain no mine, and to never reveal a mine. Bash version of the game uses a 10x10 matrix, implemented using simple bash arrays.
|
||||
|
||||
First, I assign some random variables. These are the locations that mines could be placed on the board. By limiting the number of locations, it will be easy to build on top of this. The logic could be better, but I wanted to keep the game looking simple and a bit immature. (I wrote this for fun, but I would happily welcome your contributions to make it look better.)
|
||||
|
||||
The variables below are some default variables, declared to call randomly for field placement, like the variables a-g, we will use them to calculate our extractable mines:
|
||||
|
||||
|
||||
```
|
||||
# variables
|
||||
score=0 # will be used to store the score of the game
|
||||
# variables below will be used to randomly get the extract-able cells/fields from our mine.
|
||||
a="1 10 -10 -1"
|
||||
b="-1 0 1"
|
||||
c="0 1"
|
||||
d="-1 0 1 -2 -3"
|
||||
e="1 2 20 21 10 0 -10 -20 -23 -2 -1"
|
||||
f="1 2 3 35 30 20 22 10 0 -10 -20 -25 -30 -35 -3 -2 -1"
|
||||
g="1 4 6 9 10 15 20 25 30 -30 -24 -11 -10 -9 -8 -7"
|
||||
#
|
||||
# declarations
|
||||
declare -a room # declare an array room, it will represent each cell/field of our mine.
|
||||
```
|
||||
|
||||
Next, I print my board with columns (0-9) and rows (a-j), forming a 10x10 matrix to serve as the minefield for the game. (M[10][10] is a 100-value array with indexes 0-99.) If you want to know more about Bash arrays, read [_You don't know Bash: An introduction to Bash arrays_][4].
|
||||
|
||||
Lets call it a function, **plough,** we print the header first: two blank lines, the column headings, and a line to outline the top of the playing field:
|
||||
|
||||
|
||||
```
|
||||
printf '\n\n'
|
||||
printf '%s' " a b c d e f g h i j"
|
||||
printf '\n %s\n' "-----------------------------------------"
|
||||
```
|
||||
|
||||
Next, I establish a counter variable, called **r**, to keep track of how many horizontal rows have been populated. Note that, we will use the same counter variable '**r**' as our array index later in the game code. In a [Bash **for** loop][5], using the **seq** command to increment from 0 to 9, I print a digit (**d%**) to represent the row number ($row, which is defined by **seq**):
|
||||
|
||||
|
||||
```
|
||||
r=0 # our counter
|
||||
for row in $(seq 0 9); do
|
||||
printf '%d ' "$row" # print the row numbers from 0-9
|
||||
```
|
||||
|
||||
Before we move ahead from here, lets check what we have made till now. We printed sequence **[a-j] **horizontally first and then we printed row numbers in a range **[0-9]**, we will be using these two ranges to act as our users input coordinates to locate the mine to extract.** **
|
||||
|
||||
Next,** **Within each row, there is a column intersection, so it's time to open a new **for** loop. This one manages each column, so it essentially generates each cell in the playing field. I have added some helper functions that you can see the full definition of in the source code. For each cell, we need something to make the field look like a mine, so we initialize the empty ones with a dot (.), using a custom function called [**is_null_field**][6]. Also, we need an array variable to store the value for each cell, we will use the predefined global array variable **[room][7]** along with an index [variable **r**][8]. As **r** increments, we iterate over the cells, dropping mines along the way.
|
||||
|
||||
|
||||
```
|
||||
for col in $(seq 0 9); do
|
||||
((r+=1)) # increment the counter as we move forward in column sequence
|
||||
is_null_field $r # assume a function which will check, if the field is empty, if so, initialize it with a dot(.)
|
||||
printf '%s \e[33m%s\e[0m ' "|" "${room[$r]}" # finally print the separator, note that, the first value of ${room[$r]} will be '.', as it is just initialized.
|
||||
#close col loop
|
||||
done
|
||||
```
|
||||
|
||||
Finally, I keep the board well-defined by enclosing the bottom of each row with a line, and then close the row loop:
|
||||
|
||||
|
||||
```
|
||||
printf '%s\n' "|" # print the line end separator
|
||||
printf ' %s\n' "-----------------------------------------"
|
||||
# close row for loop
|
||||
done
|
||||
printf '\n\n'
|
||||
```
|
||||
|
||||
The full **plough** function looks like:
|
||||
|
||||
|
||||
```
|
||||
plough()
|
||||
{
|
||||
r=0
|
||||
printf '\n\n'
|
||||
printf '%s' " a b c d e f g h i j"
|
||||
printf '\n %s\n' "-----------------------------------------"
|
||||
for row in $(seq 0 9); do
|
||||
printf '%d ' "$row"
|
||||
for col in $(seq 0 9); do
|
||||
((r+=1))
|
||||
is_null_field $r
|
||||
printf '%s \e[33m%s\e[0m ' "|" "${room[$r]}"
|
||||
done
|
||||
printf '%s\n' "|"
|
||||
printf ' %s\n' "-----------------------------------------"
|
||||
done
|
||||
printf '\n\n'
|
||||
}
|
||||
```
|
||||
|
||||
It took me some time to decide on needing the **is_null_field**, so let's take a closer look at what it does. We need a dependable state from the beginning of the game. That choice is arbitrary–it could have been a number or any character. I decided to assume everything was declared as a dot (.) because I believe it makes the gameboard look pretty. Here's what that looks like:
|
||||
|
||||
|
||||
```
|
||||
is_null_field()
|
||||
{
|
||||
local e=$1 # we used index 'r' for array room already, let's call it 'e'
|
||||
if [[ -z "${room[$e]}" ]];then
|
||||
room[$r]="." # this is where we put the dot(.) to initialize the cell/minefield
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
Now that, I have all the cells in our mine initialized, I get a count of all available mines by declaring and later calling a simple function shown below:
|
||||
|
||||
|
||||
```
|
||||
get_free_fields()
|
||||
{
|
||||
free_fields=0 # initialize the variable
|
||||
for n in $(seq 1 ${#room[@]}); do
|
||||
if [[ "${room[$n]}" = "." ]]; then # check if the cells has initial value dot(.), then count it as a free field.
|
||||
((free_fields+=1))
|
||||
fi
|
||||
done
|
||||
}
|
||||
```
|
||||
|
||||
Here is the printed minefield, where [**a-j]** are columns, and [**0-9**] are rows.
|
||||
|
||||
![Minefield][9]
|
||||
|
||||
### Create the logic to drive the player
|
||||
|
||||
The player logic reads an option from [stdin][10] as a coordinate to the mines and extracts the exact field on the minefield. It uses Bash's [parameter expansion][11] to extract the column and row inputs, then feeds the column to a switch that points to its equivalent integer notation on the board, to understand this, see the values getting assigned to variable '**o'** in the switch case statement below. For instance, a player might enter **c3**, which Bash splits into two characters: **c** and **3**. For simplicity, I'm skipping over how invalid entry is handled.
|
||||
|
||||
|
||||
```
|
||||
colm=${opt:0:1} # get the first char, the alphabet
|
||||
ro=${opt:1:1} # get the second char, the digit
|
||||
case $colm in
|
||||
a ) o=1;; # finally, convert the alphabet to its equivalent integer notation.
|
||||
b ) o=2;;
|
||||
c ) o=3;;
|
||||
d ) o=4;;
|
||||
e ) o=5;;
|
||||
f ) o=6;;
|
||||
g ) o=7;;
|
||||
h ) o=8;;
|
||||
i ) o=9;;
|
||||
j ) o=10;;
|
||||
esac
|
||||
```
|
||||
|
||||
Then it calculates the exact index and assigns the index of the input coordinates to that field.
|
||||
|
||||
There is also a lot of use of **shuf** command here, **shuf** is a [Linux utility][12] designed to provide a random permutation of information where the **-i** option denotes indexes or possible ranges to shuffle and **-n** denotes the maximum number or output given back. Double parentheses allow for [mathematical evaluation][13] in Bash, and we will use them heavily here.
|
||||
|
||||
Let's assume our previous example received **c3** via stdin. Then, **ro=3** and **o=3** from above switch case statement converted **c** to its equivalent integer, put it into our formula to calculate final index '**i'.**
|
||||
|
||||
|
||||
```
|
||||
i=$(((ro*10)+o)) # Follow BODMAS rule, to calculate final index.
|
||||
is_free_field $i $(shuf -i 0-5 -n 1) # call a custom function that checks if the final index value points to a an empty/free cell/field.
|
||||
```
|
||||
|
||||
Walking through this math to understand how the final index '**i**' is calculated:
|
||||
|
||||
|
||||
```
|
||||
i=$(((ro*10)+o))
|
||||
i=$(((3*10)+3))=$((30+3))=33
|
||||
```
|
||||
|
||||
The final index value is 33. On our board, printed above, the final index points to 33rd cell and that should be 3rd (starting from 0, otherwise 4th) row and 3rd (C) column.
|
||||
|
||||
### Create the logic to determine the available minefield
|
||||
|
||||
To extract a mine, after the coordinates are decoded and the index is found, the program checks whether that field is available. If it's not, the program displays a warning, and the player chooses another coordinate.
|
||||
|
||||
In this code, a cell is available if it contains a dot (**.**) character. Assuming it's available, the value in the cell is reset and the score is updated. If a cell is unavailable because it does not contain a dot, then a variable **not_allowed** is set. For brevity, I leave it to you to look at the source code of the game for the contents of [the warning statement][14] in the game logic.
|
||||
|
||||
|
||||
```
|
||||
is_free_field()
|
||||
{
|
||||
local f=$1
|
||||
local val=$2
|
||||
not_allowed=0
|
||||
if [[ "${room[$f]}" = "." ]]; then
|
||||
room[$f]=$val
|
||||
score=$((score+val))
|
||||
else
|
||||
not_allowed=1
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
![Extracting mines][15]
|
||||
|
||||
If the coordinate entered is available, the mine is discovered, as shown below. When **h6** is provided as input, some values at random populated on our minefields, these values are added to users score after the mins are extracted.
|
||||
|
||||
![Extracting mines][16]
|
||||
|
||||
Now remember the variables we declared at the start, [a-g], I will now use them here to extract random mines assigning their value to the variable **m** using Bash indirection. So, depending upon the input coordinates, the program picks a random set of additional numbers (**m**) to calculate the additional fields to be populated (as shown above) by adding them to the original input coordinates, represented here by **i (**calculated above**)**.
|
||||
|
||||
Please note the character **X** in below code snippet, is our sole GAME-OVER trigger, we added it to our shuffle list to appear at random, with the beauty of **shuf** command, it can appear after any number of chances or may not even appear for our lucky winning user.
|
||||
|
||||
|
||||
```
|
||||
m=$(shuf -e a b c d e f g X -n 1) # add an extra char X to the shuffle, when m=X, its GAMEOVER
|
||||
if [[ "$m" != "X" ]]; then # X will be our explosive mine(GAME-OVER) trigger
|
||||
for limit in ${!m}; do # !m represents the value of value of m
|
||||
field=$(shuf -i 0-5 -n 1) # again get a random number and
|
||||
index=$((i+limit)) # add values of m to our index and calculate a new index till m reaches its last element.
|
||||
is_free_field $index $field
|
||||
done
|
||||
```
|
||||
|
||||
I want all revealed cells to be contiguous to the cell selected by the player.
|
||||
|
||||
![Extracting mines][17]
|
||||
|
||||
### Keep a count of available and extracted mines
|
||||
|
||||
The program needs to keep track of available cells in the minefield; otherwise, it keeps asking the player for input even after all the cells have been revealed. To implement this, I create a variable called **free_fields**, initially setting it to 0. In a **for** loop defined by the remaining number of available cells/fields in our minefields. ****If a cell contains a dot (**.**), then the count of **free_fields** is incremented.
|
||||
|
||||
|
||||
```
|
||||
get_free_fields()
|
||||
{
|
||||
free_fields=0
|
||||
for n in $(seq 1 ${#room[@]}); do
|
||||
if [[ "${room[$n]}" = "." ]]; then
|
||||
((free_fields+=1))
|
||||
fi
|
||||
done
|
||||
}
|
||||
```
|
||||
|
||||
Wait, what if, the **free_fields=0**? That means, our user had extracted all the mines. Please feel free to look at [the exact code][18] to understand better.
|
||||
|
||||
|
||||
```
|
||||
if [[ $free_fields -eq 0 ]]; then # well that means you extracted all the mines.
|
||||
printf '\n\n\t%s: %s %d\n\n' "You Win" "you scored" "$score"
|
||||
exit 0
|
||||
fi
|
||||
```
|
||||
|
||||
### Create the logic for Gameover
|
||||
|
||||
For the Gameover situation, we print to the middle of the terminal using some [nifty logic][19] that I leave it to the reader to explore how it works.
|
||||
|
||||
|
||||
```
|
||||
if [[ "$m" = "X" ]]; then
|
||||
g=0 # to use it in parameter expansion
|
||||
room[$i]=X # override the index and print X
|
||||
for j in {42..49}; do # in the middle of the minefields,
|
||||
out="gameover"
|
||||
k=${out:$g:1} # print one alphabet in each cell
|
||||
room[$j]=${k^^}
|
||||
((g+=1))
|
||||
done
|
||||
fi
|
||||
```
|
||||
|
||||
Finally, we can print the two lines which are most awaited.
|
||||
|
||||
|
||||
```
|
||||
if [[ "$m" = "X" ]]; then
|
||||
printf '\n\n\t%s: %s %d\n' "GAMEOVER" "you scored" "$score"
|
||||
printf '\n\n\t%s\n\n' "You were just $free_fields mines away."
|
||||
exit 0
|
||||
fi
|
||||
```
|
||||
|
||||
![Minecraft Gameover][20]
|
||||
|
||||
That's it, folks! If you want to know more, access the source code for this Minesweeper game and other games in Bash from my [GitHub repo][3]. I hope it gives you some inspiration to learn more Bash and to have fun while doing so.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/9/advanced-bash-building-minesweeper
|
||||
|
||||
作者:[Abhishek Tamrakar][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/tamrakarhttps://opensource.com/users/dnearyhttps://opensource.com/users/sethhttps://opensource.com/users/sethhttps://opensource.com/users/marcobravo
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/bash_command_line.png?itok=k4z94W2U (bash logo on green background)
|
||||
[2]: https://en.wikipedia.org/wiki/Minesweeper_(video_game)
|
||||
[3]: https://github.com/abhiTamrakar/playground/tree/master/bash_games
|
||||
[4]: https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays
|
||||
[5]: https://opensource.com/article/19/6/how-write-loop-bash
|
||||
[6]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L114-L120
|
||||
[7]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L41
|
||||
[8]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L74
|
||||
[9]: https://opensource.com/sites/default/files/uploads/minefield.png (Minefield)
|
||||
[10]: https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)
|
||||
[11]: https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
|
||||
[12]: https://linux.die.net/man/1/shuf
|
||||
[13]: https://www.tldp.org/LDP/abs/html/dblparens.html
|
||||
[14]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L143-L177
|
||||
[15]: https://opensource.com/sites/default/files/uploads/extractmines.png (Extracting mines)
|
||||
[16]: https://opensource.com/sites/default/files/uploads/extractmines2.png (Extracting mines)
|
||||
[17]: https://opensource.com/sites/default/files/uploads/extractmines3.png (Extracting mines)
|
||||
[18]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L91
|
||||
[19]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L131-L141
|
||||
[20]: https://opensource.com/sites/default/files/uploads/gameover.png (Minecraft Gameover)
|
@ -0,0 +1,335 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to Run the Top Command in Batch Mode)
|
||||
[#]: via: (https://www.2daygeek.com/linux-run-execute-top-command-in-batch-mode/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
How to Run the Top Command in Batch Mode
|
||||
======
|
||||
|
||||
The **[Linux Top command][1]** is the best and most well known command that everyone uses to **[monitor Linux system performance][2]**.
|
||||
|
||||
You probably already know most of the options available, except for a few options, and if I’m not wrong, “batch more” is one of the options.
|
||||
|
||||
Most script writer and developers know this because this option is mainly used when writing the script.
|
||||
|
||||
If you’re not sure about this, don’t worry we’re here to explain this.
|
||||
|
||||
### What is “Batch Mode” in the Top Command
|
||||
|
||||
The “Batch Mode” option allows you to send top command output to other programs or to a file.
|
||||
|
||||
In this mode, top will not accept input and runs until the iterations limit you’ve set with the “-n” command-line option.
|
||||
|
||||
If you want to fix any performance issues on the Linux server, you need to **[understand the top command output][3]** correctly.
|
||||
|
||||
### 1) How to Run the Top Command in Batch Mode
|
||||
|
||||
By default, the top command sort the results based on CPU usage, so when you run the below top command in batch mode, it does the same and prints the first 35 lines.
|
||||
|
||||
```
|
||||
# top -bc | head -35
|
||||
|
||||
top - 06:41:14 up 8 days, 20:24, 1 user, load average: 0.87, 0.77, 0.81
|
||||
Tasks: 139 total, 1 running, 136 sleeping, 0 stopped, 2 zombie
|
||||
%Cpu(s): 0.0 us, 3.2 sy, 0.0 ni, 96.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
|
||||
KiB Mem : 3880940 total, 1595932 free, 886736 used, 1398272 buff/cache
|
||||
KiB Swap: 1048572 total, 514640 free, 533932 used. 2648472 avail Mem
|
||||
|
||||
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
||||
1 root 20 0 191144 2800 1596 S 0.0 0.1 5:43.63 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
|
||||
2 root 20 0 0 0 0 S 0.0 0.0 0:00.32 [kthreadd]
|
||||
3 root 20 0 0 0 0 S 0.0 0.0 0:28.10 [ksoftirqd/0]
|
||||
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kworker/0:0H]
|
||||
7 root rt 0 0 0 0 S 0.0 0.0 0:33.96 [migration/0]
|
||||
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 [rcu_bh]
|
||||
9 root 20 0 0 0 0 S 0.0 0.0 63:05.12 [rcu_sched]
|
||||
10 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [lru-add-drain]
|
||||
11 root rt 0 0 0 0 S 0.0 0.0 0:08.79 [watchdog/0]
|
||||
12 root rt 0 0 0 0 S 0.0 0.0 0:08.82 [watchdog/1]
|
||||
13 root rt 0 0 0 0 S 0.0 0.0 0:44.27 [migration/1]
|
||||
14 root 20 0 0 0 0 S 0.0 0.0 1:22.45 [ksoftirqd/1]
|
||||
16 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kworker/1:0H]
|
||||
18 root 20 0 0 0 0 S 0.0 0.0 0:00.01 [kdevtmpfs]
|
||||
19 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [netns]
|
||||
20 root 20 0 0 0 0 S 0.0 0.0 0:01.35 [khungtaskd]
|
||||
21 root 0 -20 0 0 0 S 0.0 0.0 0:00.02 [writeback]
|
||||
22 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kintegrityd]
|
||||
23 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [bioset]
|
||||
24 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kblockd]
|
||||
25 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [md]
|
||||
26 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [edac-poller]
|
||||
33 root 20 0 0 0 0 S 0.0 0.0 1:19.07 [kswapd0]
|
||||
34 root 25 5 0 0 0 S 0.0 0.0 0:00.00 [ksmd]
|
||||
35 root 39 19 0 0 0 S 0.0 0.0 0:12.80 [khugepaged]
|
||||
36 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [crypto]
|
||||
44 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kthrotld]
|
||||
46 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kmpath_rdacd]
|
||||
```
|
||||
|
||||
### 2) How to Run the Top Command in Batch Mode and Sort the Output Based on Memory Usage
|
||||
|
||||
Run the below top command to sort the results based on memory usage in batch mode.
|
||||
|
||||
```
|
||||
# top -bc -o +%MEM | head -n 20
|
||||
|
||||
top - 06:42:00 up 8 days, 20:25, 1 user, load average: 0.66, 0.74, 0.80
|
||||
Tasks: 146 total, 1 running, 145 sleeping, 0 stopped, 0 zombie
|
||||
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
|
||||
KiB Mem : 3880940 total, 1422044 free, 1059176 used, 1399720 buff/cache
|
||||
KiB Swap: 1048572 total, 514640 free, 533932 used. 2475984 avail Mem
|
||||
|
||||
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
||||
18105 mysql 20 0 1453900 156096 8816 S 0.0 4.0 2:12.98 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
|
||||
1841 root 20 0 228980 107036 5360 S 0.0 2.8 0:05.56 /usr/local/cpanel/3rdparty/perl/528/bin/perl -T -w /usr/local/cpanel/3rdparty/bin/spamd --max-children=3 --max-spare=1 --allowed-ips=127.0.0.+
|
||||
4301 root 20 0 230208 104608 1816 S 0.0 2.7 0:03.77 spamd child
|
||||
8139 nobody 20 0 257000 27108 3408 S 0.0 0.7 0:00.04 /usr/sbin/httpd -k start
|
||||
7961 nobody 20 0 256988 26912 3160 S 0.0 0.7 0:00.05 /usr/sbin/httpd -k start
|
||||
8190 nobody 20 0 256976 26812 3140 S 0.0 0.7 0:00.05 /usr/sbin/httpd -k start
|
||||
8353 nobody 20 0 256976 26812 3144 S 0.0 0.7 0:00.04 /usr/sbin/httpd -k start
|
||||
8629 nobody 20 0 256856 26736 3108 S 0.0 0.7 0:00.02 /usr/sbin/httpd -k start
|
||||
8636 nobody 20 0 256856 26712 3100 S 0.0 0.7 0:00.03 /usr/sbin/httpd -k start
|
||||
8611 nobody 20 0 256844 25764 2228 S 0.0 0.7 0:00.01 /usr/sbin/httpd -k start
|
||||
8451 nobody 20 0 256844 25760 2220 S 0.0 0.7 0:00.04 /usr/sbin/httpd -k start
|
||||
8610 nobody 20 0 256844 25748 2224 S 0.0 0.7 0:00.01 /usr/sbin/httpd -k start
|
||||
8632 nobody 20 0 256844 25744 2216 S 0.0 0.7 0:00.03 /usr/sbin/httpd -k start
|
||||
```
|
||||
|
||||
**Details of the above command:**
|
||||
|
||||
* **-b :** Batch mode operation
|
||||
* **-c :** To print the absolute path of the running process
|
||||
* **-o :** To specify fields for sorting processes
|
||||
* **head :** Output the first part of files
|
||||
* **-n :** To print the first “n” lines
|
||||
|
||||
|
||||
|
||||
### 3) How to Run the Top Command in Batch Mode and Sort the Output Based on a Specific User Process
|
||||
|
||||
If you want to sort results based on a specific user, run the below top command.
|
||||
|
||||
```
|
||||
# top -bc -u mysql | head -n 10
|
||||
|
||||
top - 06:44:58 up 8 days, 20:27, 1 user, load average: 0.99, 0.87, 0.84
|
||||
Tasks: 140 total, 1 running, 137 sleeping, 0 stopped, 2 zombie
|
||||
%Cpu(s): 13.3 us, 3.3 sy, 0.0 ni, 83.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
|
||||
KiB Mem : 3880940 total, 1589832 free, 885648 used, 1405460 buff/cache
|
||||
KiB Swap: 1048572 total, 514640 free, 533932 used. 2649412 avail Mem
|
||||
|
||||
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
||||
18105 mysql 20 0 1453900 156888 8816 S 0.0 4.0 2:16.42 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
|
||||
```
|
||||
|
||||
### 4) How to Run the Top Command in Batch Mode and Sort the Output Based on the Process Age
|
||||
|
||||
Use the below top command to sort the results based on the age of the process in batch mode. It shows the total CPU time the task has used since it started.
|
||||
|
||||
But if you want to check how long a process has been running on Linux, go to the following article.
|
||||
|
||||
* **[Five Ways to Check How Long a Process Has Been Running in Linux][4]**
|
||||
|
||||
|
||||
|
||||
```
|
||||
# top -bc -o TIME+ | head -n 20
|
||||
|
||||
top - 06:45:56 up 8 days, 20:28, 1 user, load average: 0.56, 0.77, 0.81
|
||||
Tasks: 148 total, 1 running, 146 sleeping, 0 stopped, 1 zombie
|
||||
%Cpu(s): 0.0 us, 3.1 sy, 0.0 ni, 96.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
|
||||
KiB Mem : 3880940 total, 1378664 free, 1094876 used, 1407400 buff/cache
|
||||
KiB Swap: 1048572 total, 514640 free, 533932 used. 2440332 avail Mem
|
||||
|
||||
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
||||
9 root 20 0 0 0 0 S 0.0 0.0 63:05.70 [rcu_sched]
|
||||
272 root 20 0 0 0 0 S 0.0 0.0 16:12.13 [xfsaild/vda1]
|
||||
3882 root 20 0 229832 6212 1220 S 0.0 0.2 9:00.84 /usr/sbin/httpd -k start
|
||||
1 root 20 0 191144 2800 1596 S 0.0 0.1 5:43.75 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
|
||||
3761 root 20 0 68784 9820 2048 S 0.0 0.3 5:09.67 tailwatchd
|
||||
3529 root 20 0 404380 3472 2604 S 0.0 0.1 3:24.98 /usr/sbin/rsyslogd -n
|
||||
3520 root 20 0 574208 572 164 S 0.0 0.0 3:07.74 /usr/bin/python2 -Es /usr/sbin/tuned -l -P
|
||||
444 dbus 20 0 58444 1144 612 S 0.0 0.0 2:23.90 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
|
||||
18105 mysql 20 0 1453900 157152 8816 S 0.0 4.0 2:17.29 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
|
||||
249 root 0 -20 0 0 0 S 0.0 0.0 1:28.83 [kworker/0:1H]
|
||||
14 root 20 0 0 0 0 S 0.0 0.0 1:22.46 [ksoftirqd/1]
|
||||
33 root 20 0 0 0 0 S 0.0 0.0 1:19.07 [kswapd0]
|
||||
342 root 20 0 39472 2940 2752 S 0.0 0.1 1:18.17 /usr/lib/systemd/systemd-journald
|
||||
```
|
||||
|
||||
### 5) How to Run the Top Command in Batch Mode and Save the Output to a File
|
||||
|
||||
If you want to share the output of the top command to someone for troubleshooting purposes, redirect the output to a file using the following command.
|
||||
|
||||
```
|
||||
# top -bc | head -35 > top-report.txt
|
||||
|
||||
# cat top-report.txt
|
||||
|
||||
top - 06:47:11 up 8 days, 20:30, 1 user, load average: 0.67, 0.77, 0.81
|
||||
Tasks: 133 total, 4 running, 129 sleeping, 0 stopped, 0 zombie
|
||||
%Cpu(s): 59.4 us, 12.5 sy, 0.0 ni, 28.1 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
|
||||
KiB Mem : 3880940 total, 1596268 free, 843284 used, 1441388 buff/cache
|
||||
KiB Swap: 1048572 total, 514640 free, 533932 used. 2659084 avail Mem
|
||||
|
||||
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
||||
9686 daygeekc 20 0 406132 62184 43448 R 94.1 1.6 0:00.34 /opt/cpanel/ea-php56/root/usr/bin/php-cgi
|
||||
9689 nobody 20 0 256588 24428 1184 S 5.9 0.6 0:00.01 /usr/sbin/httpd -k start
|
||||
1 root 20 0 191144 2800 1596 S 0.0 0.1 5:43.79 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
|
||||
2 root 20 0 0 0 0 S 0.0 0.0 0:00.32 [kthreadd]
|
||||
3 root 20 0 0 0 0 S 0.0 0.0 0:28.11 [ksoftirqd/0]
|
||||
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kworker/0:0H]
|
||||
7 root rt 0 0 0 0 S 0.0 0.0 0:33.96 [migration/0]
|
||||
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 [rcu_bh]
|
||||
9 root 20 0 0 0 0 R 0.0 0.0 63:05.82 [rcu_sched]
|
||||
10 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [lru-add-drain]
|
||||
11 root rt 0 0 0 0 S 0.0 0.0 0:08.79 [watchdog/0]
|
||||
12 root rt 0 0 0 0 S 0.0 0.0 0:08.82 [watchdog/1]
|
||||
13 root rt 0 0 0 0 S 0.0 0.0 0:44.28 [migration/1]
|
||||
14 root 20 0 0 0 0 S 0.0 0.0 1:22.46 [ksoftirqd/1]
|
||||
16 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kworker/1:0H]
|
||||
18 root 20 0 0 0 0 S 0.0 0.0 0:00.01 [kdevtmpfs]
|
||||
19 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [netns]
|
||||
20 root 20 0 0 0 0 S 0.0 0.0 0:01.35 [khungtaskd]
|
||||
21 root 0 -20 0 0 0 S 0.0 0.0 0:00.02 [writeback]
|
||||
22 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kintegrityd]
|
||||
23 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [bioset]
|
||||
24 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kblockd]
|
||||
25 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [md]
|
||||
26 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [edac-poller]
|
||||
33 root 20 0 0 0 0 S 0.0 0.0 1:19.07 [kswapd0]
|
||||
34 root 25 5 0 0 0 S 0.0 0.0 0:00.00 [ksmd]
|
||||
35 root 39 19 0 0 0 S 0.0 0.0 0:12.80 [khugepaged]
|
||||
36 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [crypto]
|
||||
```
|
||||
|
||||
### How to Sort Output Based on Specific Fields
|
||||
|
||||
In the latest version of the top command release, press the **“f”** key to sort the fields via the field letter.
|
||||
|
||||
To sort with a new field, use the **“up/down”** arrow to select the correct selection, and then press **“s”** to sort it. Finally press **“q”** to exit from this window.
|
||||
|
||||
```
|
||||
Fields Management for window 1:Def, whose current sort field is %CPU
|
||||
Navigate with Up/Dn, Right selects for move then or Left commits,
|
||||
'd' or toggles display, 's' sets sort. Use 'q' or to end!
|
||||
PID = Process Id nsUTS = UTS namespace Inode
|
||||
USER = Effective User Name LXC = LXC container name
|
||||
PR = Priority RSan = RES Anonymous (KiB)
|
||||
NI = Nice Value RSfd = RES File-based (KiB)
|
||||
VIRT = Virtual Image (KiB) RSlk = RES Locked (KiB)
|
||||
RES = Resident Size (KiB) RSsh = RES Shared (KiB)
|
||||
SHR = Shared Memory (KiB) CGNAME = Control Group name
|
||||
S = Process Status NU = Last Used NUMA node
|
||||
%CPU = CPU Usage
|
||||
%MEM = Memory Usage (RES)
|
||||
TIME+ = CPU Time, hundredths
|
||||
COMMAND = Command Name/Line
|
||||
PPID = Parent Process pid
|
||||
UID = Effective User Id
|
||||
RUID = Real User Id
|
||||
RUSER = Real User Name
|
||||
SUID = Saved User Id
|
||||
SUSER = Saved User Name
|
||||
GID = Group Id
|
||||
GROUP = Group Name
|
||||
PGRP = Process Group Id
|
||||
TTY = Controlling Tty
|
||||
TPGID = Tty Process Grp Id
|
||||
SID = Session Id
|
||||
nTH = Number of Threads
|
||||
P = Last Used Cpu (SMP)
|
||||
TIME = CPU Time
|
||||
SWAP = Swapped Size (KiB)
|
||||
CODE = Code Size (KiB)
|
||||
DATA = Data+Stack (KiB)
|
||||
nMaj = Major Page Faults
|
||||
nMin = Minor Page Faults
|
||||
nDRT = Dirty Pages Count
|
||||
WCHAN = Sleeping in Function
|
||||
Flags = Task Flags
|
||||
CGROUPS = Control Groups
|
||||
SUPGIDS = Supp Groups IDs
|
||||
SUPGRPS = Supp Groups Names
|
||||
TGID = Thread Group Id
|
||||
OOMa = OOMEM Adjustment
|
||||
OOMs = OOMEM Score current
|
||||
ENVIRON = Environment vars
|
||||
vMj = Major Faults delta
|
||||
vMn = Minor Faults delta
|
||||
USED = Res+Swap Size (KiB)
|
||||
nsIPC = IPC namespace Inode
|
||||
nsMNT = MNT namespace Inode
|
||||
nsNET = NET namespace Inode
|
||||
nsPID = PID namespace Inode
|
||||
nsUSER = USER namespace Inode
|
||||
```
|
||||
|
||||
For older version of the top command, press the **“shift+f”** or **“shift+o”** key to sort the fields via the field letter.
|
||||
|
||||
To sort with a new field, select the corresponding sort **field letter**, and then press **“Enter”** to sort it.
|
||||
|
||||
```
|
||||
Current Sort Field: N for window 1:Def
|
||||
Select sort field via field letter, type any other key to return
|
||||
a: PID = Process Id
|
||||
b: PPID = Parent Process Pid
|
||||
c: RUSER = Real user name
|
||||
d: UID = User Id
|
||||
e: USER = User Name
|
||||
f: GROUP = Group Name
|
||||
g: TTY = Controlling Tty
|
||||
h: PR = Priority
|
||||
i: NI = Nice value
|
||||
j: P = Last used cpu (SMP)
|
||||
k: %CPU = CPU usage
|
||||
l: TIME = CPU Time
|
||||
m: TIME+ = CPU Time, hundredths
|
||||
* N: %MEM = Memory usage (RES)
|
||||
o: VIRT = Virtual Image (kb)
|
||||
p: SWAP = Swapped size (kb)
|
||||
q: RES = Resident size (kb)
|
||||
r: CODE = Code size (kb)
|
||||
s: DATA = Data+Stack size (kb)
|
||||
t: SHR = Shared Mem size (kb)
|
||||
u: nFLT = Page Fault count
|
||||
v: nDRT = Dirty Pages count
|
||||
w: S = Process Status
|
||||
x: COMMAND = Command name/line
|
||||
y: WCHAN = Sleeping in Function
|
||||
z: Flags = Task Flags
|
||||
Note1:
|
||||
If a selected sort field can't be
|
||||
shown due to screen width or your
|
||||
field order, the '<' and '>' keys
|
||||
will be unavailable until a field
|
||||
within viewable range is chosen.
|
||||
Note2:
|
||||
Field sorting uses internal values,
|
||||
not those in column display. Thus,
|
||||
the TTY & WCHAN fields will violate
|
||||
strict ASCII collating sequence.
|
||||
(shame on you if WCHAN is chosen)
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/linux-run-execute-top-command-in-batch-mode/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/linux-top-command-linux-system-performance-monitoring-tool/
|
||||
[2]: https://www.2daygeek.com/category/system-monitoring/
|
||||
[3]: https://www.2daygeek.com/understanding-linux-top-command-output-usage/
|
||||
[4]: https://www.2daygeek.com/how-to-check-how-long-a-process-has-been-running-in-linux/
|
@ -0,0 +1,334 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wenwensnow)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Hone advanced Bash skills by building Minesweeper)
|
||||
[#]: via: (https://opensource.com/article/19/9/advanced-bash-building-minesweeper)
|
||||
[#]: author: (Abhishek Tamrakar https://opensource.com/users/tamrakarhttps://opensource.com/users/dnearyhttps://opensource.com/users/sethhttps://opensource.com/users/sethhttps://opensource.com/users/marcobravo)
|
||||
|
||||
通过编写扫雷游戏提高你的bash技巧
|
||||
======
|
||||
那些令人怀念的经典游戏可是提高编程能力的好素材。今天就让我们仔细探索一番,怎么用Bash编写一个扫雷程序。
|
||||
![bash logo on green background][1]
|
||||
|
||||
我在编程教学方面不是专家,但当我想更好掌握某一样东西时,会试着找出让自己乐在其中的方法。比方说,当我想在shell编程方面更进一步时,我决定用Bash编写一个[扫雷][2]游戏来加以练习。
|
||||
|
||||
如果你是一个有经验的Bash程序员,并且在提高技巧的同时乐在其中,你可以在终端中编写个人版本的扫雷。完整代码可以在这个 [GitHub 库]中找到[3].
|
||||
|
||||
### 做好准备
|
||||
|
||||
在我编写任何代码之前,我列出了游戏所必须的几个部分:
|
||||
|
||||
1. 显示雷区
|
||||
2. 创建玩家逻辑
|
||||
3. 创建判断单元格是否可选的逻辑
|
||||
4. 记录已选择和可用单元格的个数
|
||||
5. 创建游戏结束逻辑
|
||||
|
||||
|
||||
### 显示雷区
|
||||
|
||||
在扫雷中,游戏界面是一个由2D数组(列和行)组成的不透明小方格。每一格下都有可能藏有地雷。玩家的任务就是找到那些不含雷的方格,并且在这一过程中,不能点到地雷。Bash版本的扫雷使用10x10的矩阵,实际逻辑则由一个简单的Bash数组来完成。
|
||||
|
||||
首先,我先生成了一些随机数字。这将是地雷在雷区里的位置。为了控制地雷的数量,在开始编写代码之前,这么做会容易一些。实现这一功能的逻辑可以更好,但我这么做,是为了让游戏实现保持简洁,并有改进空间。(我编写这个游戏纯属娱乐,但如果你能将它修改的更好,我也是很乐意的。)
|
||||
|
||||
下面这些变量是整个过程中是不变的,声明它们是为了随机生成数字。就像下面的变量a-g,它们会被用来计算可选择的地雷
|
||||
的值:
|
||||
|
||||
```
|
||||
# 变量
|
||||
score=0 # 会用来存放游戏分数
|
||||
#下面这些变量,用来随机生成可选择地雷的实际值
|
||||
a="1 10 -10 -1"
|
||||
b="-1 0 1"
|
||||
c="0 1"
|
||||
d="-1 0 1 -2 -3"
|
||||
e="1 2 20 21 10 0 -10 -20 -23 -2 -1"
|
||||
f="1 2 3 35 30 20 22 10 0 -10 -20 -25 -30 -35 -3 -2 -1"
|
||||
g="1 4 6 9 10 15 20 25 30 -30 -24 -11 -10 -9 -8 -7"
|
||||
#
|
||||
# 声明
|
||||
declare -a room # 声明一个room 数组,它用来表示雷区的每一格。
|
||||
```
|
||||
|
||||
接下来,我会用列(0-9)和行(a-j)显示出游戏界面,并且使用一个10x10矩阵作为雷区。(M[10][10] 是一个索引从0-99,有100个值的数组。) 如想了解更多关于Bash 数组的内容,请阅读这本书[_那些关于Bash你所不了解的事: Bash数组简介_][4]。
|
||||
|
||||
|
||||
创建一个叫 **plough**的函数,我们先将标题显示出来:两个空行,列头,和一行 “-”,以示意往下是游戏界面:
|
||||
|
||||
|
||||
```
|
||||
printf '\n\n'
|
||||
printf '%s' " a b c d e f g h i j"
|
||||
printf '\n %s\n' "-----------------------------------------"
|
||||
```
|
||||
|
||||
然后,我初始化一个计数器变量,叫 **r**,它会用来记录已显示多少横行。 注意,稍后在游戏代码中,我们会用同一个变量**r**,作为我们的数组索引。 在 [Bash **for** 循环][5]中,用 **seq**命令从0增加到9。我用 (**d%**)占位,来显示行号($row,被**seq**定义的变量)
|
||||
|
||||
|
||||
```
|
||||
r=0 # our counter
|
||||
for row in $(seq 0 9); do
|
||||
printf '%d ' "$row" # 显示 行数 0-9
|
||||
```
|
||||
|
||||
在我们接着往下做之前,让我们看看到现在都做了什么。我们先横着显示 **[a-j]** 然后再将 **[0-9]** 的行号显示出来,我们会用这两个范围,来确定用户选择的确切位置。
|
||||
|
||||
接着,在每行中,插入列,所以是时候写一个新的 **for** 循环了。 这一循环管理着每一列,也就是说,实际上是生成游戏界面的每一格。我添加了一些说明函数,你能在源码中看到它的完整实现。 对每一格来说,我们需要一些让它看起来像地雷的东西,所以我们先用一个点(.)来初始化空格。实现这一想法,我们用的是一个叫[**is_null_field**][6] 的自定义函数。 同时,我们需要一个存储每一格具体值的数组,这儿会用到之前已定义的全局数组 **[room][7]** , 并用 [变量 **r**][8]作为索引。 随着 **r** 的增加,遍历所有单元格,并随机部署地雷。
|
||||
|
||||
```
|
||||
for col in $(seq 0 9); do
|
||||
((r+=1)) # 循环完一列行数加一
|
||||
is_null_field $r # 假设这里有个函数,它会检查单元格是否为空,为真,则此单元格初始值为点(.)
|
||||
printf '%s \e[33m%s\e[0m ' "|" "${room[$r]}" # 最后显示分隔符,注意,${room[$r]} 的第一个值为 '.',等于其初始值。
|
||||
#结束 col 循环
|
||||
done
|
||||
```
|
||||
|
||||
最后,为了保持游戏界面整齐好看,我会在每行用一个竖线作为结尾,并在最后结束行循环:
|
||||
|
||||
```
|
||||
printf '%s\n' "|" #显示出行分隔符
|
||||
printf ' %s\n' "-----------------------------------------"
|
||||
# 结束行循环
|
||||
done
|
||||
printf '\n\n'
|
||||
```
|
||||
|
||||
完整的 **plough** 代码如下:
|
||||
|
||||
```
|
||||
plough()
|
||||
{
|
||||
r=0
|
||||
printf '\n\n'
|
||||
printf '%s' " a b c d e f g h i j"
|
||||
printf '\n %s\n' "-----------------------------------------"
|
||||
for row in $(seq 0 9); do
|
||||
printf '%d ' "$row"
|
||||
for col in $(seq 0 9); do
|
||||
((r+=1))
|
||||
is_null_field $r
|
||||
printf '%s \e[33m%s\e[0m ' "|" "${room[$r]}"
|
||||
done
|
||||
printf '%s\n' "|"
|
||||
printf ' %s\n' "-----------------------------------------"
|
||||
done
|
||||
printf '\n\n'
|
||||
}
|
||||
```
|
||||
|
||||
我花了点时间来思考,**is_null_field** 的具体功能是什么。让我们来看看,它到底能做些什么。在最开始,我们需要游戏有一个固定的状态。你可以随便选择所有格子的初始值,可以是一个数字或者任意字符。 我最后决定,所有单元格的初始值为一个点(.),因为我觉得,这样会让游戏界面更好看。下面就是这一函数的完整代码:
|
||||
|
||||
```
|
||||
is_null_field()
|
||||
{
|
||||
local e=$1 # 在数组room中,我们已经用过循环变量 'r'了,这次我们用'e'
|
||||
if [[ -z "${room[$e]}" ]];then
|
||||
room[$r]="." #这里用点(.)来初始化每一个单元格
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
现在,我已经初始化了所有的格子,现在只要用一个很简单的函数,就能得出,当前游戏中还有多少单元格可以操作:
|
||||
|
||||
```
|
||||
get_free_fields()
|
||||
{
|
||||
free_fields=0 # 初始化变量
|
||||
for n in $(seq 1 ${#room[@]}); do
|
||||
if [[ "${room[$n]}" = "." ]]; then # 检查当前单元格是否等于初始值(.),结果为真,则记为空余格子。
|
||||
((free_fields+=1))
|
||||
fi
|
||||
done
|
||||
}
|
||||
```
|
||||
|
||||
这是显示出来的游戏界面,[**a-j]** 为列, [**0-9**] 为行。
|
||||
![Minefield][9]
|
||||
|
||||
### 创建玩家逻辑
|
||||
|
||||
玩家操作背后的逻辑在于,先从[stdin][10] 中读取数据作为坐标,然后再找出对应位置实际包含的值。这里用到了Bash的[参数扩展][11],来设法得到行列数。然后将代表列数的字母传给switch,从而得到其对应的列数。为了更好地理解这一过程,可以看看下面这段代码中,变量 '**o'** 所对应的值。 举个例子,玩家输入了 **c3** ,这时 Bash 将其分成两个字符: **c** and **3** 。 为了简单起见,我跳过了如何处理无效输入的部分。
|
||||
|
||||
```
|
||||
colm=${opt:0:1} # 得到第一个字符,一个字母
|
||||
ro=${opt:1:1} # 得到第二个字符,一个整数
|
||||
case $colm in
|
||||
a ) o=1;; # 最后,通过字母得到对应列数。
|
||||
b ) o=2;;
|
||||
c ) o=3;;
|
||||
d ) o=4;;
|
||||
e ) o=5;;
|
||||
f ) o=6;;
|
||||
g ) o=7;;
|
||||
h ) o=8;;
|
||||
i ) o=9;;
|
||||
j ) o=10;;
|
||||
esac
|
||||
```
|
||||
|
||||
下面的代码会计算,用户所选单元格实际对应的数字,然后将结果储存在变量中。
|
||||
|
||||
这里也用到了很多的 **shuf** 命令,**shuf** 是一个专门用来生成随机序列的[Linux命令][12]。 **-i** 选项,后面需要提供需要打乱的数或者范围, **-n** 选择则规定,输出结果最多需要返回几个值。Bash中,可以在两个圆括号内进行[数学计算],这里我们会多次用到。
|
||||
|
||||
还是沿用之前的例子,玩家输入了 **c3** 。 接着,它被转化成了**ro=3** 和 **o=3**。 之后,通过上面的switch 代码, 将**c** 转化为对应的整数,带进公式,以得到最终结果 '**i'.** 的值。
|
||||
|
||||
|
||||
```
|
||||
i=$(((ro*10)+o)) # 遵循运算规则,算出最终值
|
||||
is_free_field $i $(shuf -i 0-5 -n 1) # 调用自定义函数,判断其指向空/可选择单元格。
|
||||
```
|
||||
|
||||
仔细观察这个计算过程,看看最终结果 '**i**' 是如何计算出来的:
|
||||
|
||||
```
|
||||
i=$(((ro*10)+o))
|
||||
i=$(((3*10)+3))=$((30+3))=33
|
||||
```
|
||||
|
||||
最后结果是33。在我们的游戏界面显示出来,玩家输入坐标指向了第33个单元格,也就是在第3行(从0开始,否则这里变成4),第3列。
|
||||
|
||||
### 创建判断单元格是否可选的逻辑
|
||||
|
||||
为了找到地雷,在将坐标转化,并找到实际位置之后,程序会检查这一单元格是否可选。如不可选,程序会显示一条警告信息,并要求玩家重新输入坐标。
|
||||
|
||||
在这段代码中,单元格是否可选,是由数组里对应的值是否为点(**.**)决定的。 如果可选,则重置单元格对应的值,并更新分数。反之,因为其对应值不为点,则设置 变量 **not_allowed**。 为简单起见,游戏中[警告消息][14]这部分源码,我会留给读者们自己去探索。
|
||||
|
||||
```
|
||||
is_free_field()
|
||||
{
|
||||
local f=$1
|
||||
local val=$2
|
||||
not_allowed=0
|
||||
if [[ "${room[$f]}" = "." ]]; then
|
||||
room[$f]=$val
|
||||
score=$((score+val))
|
||||
else
|
||||
not_allowed=1
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
![Extracting mines][15]
|
||||
|
||||
如输入坐标有效,且对应位置为地雷,如下图所示。 玩家输入 **h6**,游戏界面会出现一些随机生成的值。在发现地雷后,这些值会被加入用户得分。
|
||||
|
||||
|
||||
![Extracting mines][16]
|
||||
|
||||
还记得我们开头定义的变量,[a-g]吗,我会用它们来确定,随机生成地雷的具体值。 所以,根据玩家输入坐标,程序会根据 (**m**) 中随机生成的数,来生成周围其他单元格的值。(如上图所示) 。之后将所有值和初始输入坐标相加,最后结果放在**i (**计算结果如上**)**中.
|
||||
|
||||
|
||||
|
||||
请注意下面代码中的 **X**,它是我们唯一的游戏结束标志。我们将它添加到随机列表中。在 **shuf** 命令的魔力下,X可以在任意情况下出现,但如果你足够幸运的话,也可能一直不会出现。
|
||||
|
||||
```
|
||||
m=$(shuf -e a b c d e f g X -n 1) # 将 X 添加到随机列表中,当 m=X,游戏结束
|
||||
if [[ "$m" != "X" ]]; then # X将会是我们爆炸地雷(游戏结束)的触发标志
|
||||
for limit in ${!m}; do # !m 代表m变量的值
|
||||
field=$(shuf -i 0-5 -n 1) # 然后再次获得一个随机数字
|
||||
index=$((i+limit)) # 将m中的每一个值和index加起来,直到列表结尾
|
||||
is_free_field $index $field
|
||||
done
|
||||
```
|
||||
|
||||
我想要游戏界面中,所有随机显示出来的单元格,都靠近玩家选择的单元格。
|
||||
|
||||
![Extracting mines][17]
|
||||
|
||||
### 记录已选择和可用单元格的个数
|
||||
|
||||
这个程序需要记录,游戏界面中哪些单元格是可选择的。否则,程序会一直让用户输入数据,即使所有单元格都被选中过。为了实现这一功能,我创建了一个叫 **free_fields** 的变量,初始值为0。 用一个 **for** 循环,记录下游戏界面中可选择单元格的数量。 ****如果单元格所对应的值为点 (**.**), 则 **free_fields** 加一。
|
||||
|
||||
|
||||
|
||||
```
|
||||
get_free_fields()
|
||||
{
|
||||
free_fields=0
|
||||
for n in $(seq 1 ${#room[@]}); do
|
||||
if [[ "${room[$n]}" = "." ]]; then
|
||||
((free_fields+=1))
|
||||
fi
|
||||
done
|
||||
}
|
||||
```
|
||||
|
||||
等下,如果 **free_fields=0** 呢? 这意味着,玩家已选择过所有单元格。如果想更好理解这一部分,可以看看这里的[源代码][18] 。
|
||||
|
||||
|
||||
```
|
||||
if [[ $free_fields -eq 0 ]]; then # 这意味着你已选择过所有格子
|
||||
printf '\n\n\t%s: %s %d\n\n' "You Win" "you scored" "$score"
|
||||
exit 0
|
||||
fi
|
||||
```
|
||||
|
||||
### 创建游戏结束逻辑
|
||||
|
||||
对于游戏结束这种情况,我们这里使用了一些很巧妙的技巧,将结果在屏幕中央显示出来。我把这部分留给读者朋友们自己去探索。
|
||||
|
||||
|
||||
|
||||
```
|
||||
if [[ "$m" = "X" ]]; then
|
||||
g=0 # 为了在参数扩展中使用它
|
||||
room[$i]=X # 覆盖此位置原有的值,并将其赋值为X
|
||||
for j in {42..49}; do # 在游戏界面中央,
|
||||
out="gameover"
|
||||
k=${out:$g:1} # 在每一格中显示一个字母
|
||||
room[$j]=${k^^}
|
||||
((g+=1))
|
||||
done
|
||||
fi
|
||||
```
|
||||
|
||||
最后,我们显示出玩家最关心的两行。
|
||||
|
||||
```
|
||||
if [[ "$m" = "X" ]]; then
|
||||
printf '\n\n\t%s: %s %d\n' "GAMEOVER" "you scored" "$score"
|
||||
printf '\n\n\t%s\n\n' "You were just $free_fields mines away."
|
||||
exit 0
|
||||
fi
|
||||
```
|
||||
|
||||
![Minecraft Gameover][20]
|
||||
|
||||
文章到这里就结束了,朋友们! 如果你想了解更多,具体可以查看我的[GitHub 库][3],那儿有这个扫雷游戏的源代码,并且你还能找到更多用Bash 编写的游戏。 我希望,这篇文章能激起你学习Bash的兴趣,并乐在其中。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/9/advanced-bash-building-minesweeper
|
||||
|
||||
作者:[Abhishek Tamrakar][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/tamrakarhttps://opensource.com/users/dnearyhttps://opensource.com/users/sethhttps://opensource.com/users/sethhttps://opensource.com/users/marcobravo
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/bash_command_line.png?itok=k4z94W2U (bash logo on green background)
|
||||
[2]: https://en.wikipedia.org/wiki/Minesweeper_(video_game)
|
||||
[3]: https://github.com/abhiTamrakar/playground/tree/master/bash_games
|
||||
[4]: https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays
|
||||
[5]: https://opensource.com/article/19/6/how-write-loop-bash
|
||||
[6]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L114-L120
|
||||
[7]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L41
|
||||
[8]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L74
|
||||
[9]: https://opensource.com/sites/default/files/uploads/minefield.png (Minefield)
|
||||
[10]: https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)
|
||||
[11]: https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
|
||||
[12]: https://linux.die.net/man/1/shuf
|
||||
[13]: https://www.tldp.org/LDP/abs/html/dblparens.html
|
||||
[14]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L143-L177
|
||||
[15]: https://opensource.com/sites/default/files/uploads/extractmines.png (Extracting mines)
|
||||
[16]: https://opensource.com/sites/default/files/uploads/extractmines2.png (Extracting mines)
|
||||
[17]: https://opensource.com/sites/default/files/uploads/extractmines3.png (Extracting mines)
|
||||
[18]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L91
|
||||
[19]: https://github.com/abhiTamrakar/playground/blob/28143053ced699c80569666f25268e8b96c38c46/bash_games/minesweeper.sh#L131-L141
|
||||
[20]: https://opensource.com/sites/default/files/uploads/gameover.png (Minecraft Gameover)
|
Loading…
Reference in New Issue
Block a user