mirror of
https://github.com/LCTT/TranslateProject.git
synced 2024-12-26 21:30:55 +08:00
chunibyo-wly 发布 (#18017)
* 20/04/03 * 20/04/03 * 20/04/06 * 190612 翻译完成 * 审阅完成 * 发布
This commit is contained in:
parent
ed9e5d5f51
commit
9e367f35a8
@ -1,282 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (chunibyo-wly)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to write a loop in Bash)
|
||||
[#]: via: (https://opensource.com/article/19/6/how-write-loop-bash)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth/users/goncasousa/users/howtopamm/users/howtopamm/users/seth/users/wavesailor/users/seth)
|
||||
|
||||
How to write a loop in Bash
|
||||
======
|
||||
Automatically perform a set of actions on multiple files with for loops
|
||||
and find commands.
|
||||
![bash logo on green background][1]
|
||||
|
||||
A common reason people want to learn the Unix shell is to unlock the power of batch processing. If you want to perform some set of actions on many files, one of the ways to do that is by constructing a command that iterates over those files. In programming terminology, this is called _execution control,_ and one of the most common examples of it is the **for** loop.
|
||||
|
||||
A **for** loop is a recipe detailing what actions you want your computer to take _for_ each data object (such as a file) you specify.
|
||||
|
||||
### The classic for loop
|
||||
|
||||
An easy loop to try is one that analyzes a collection of files. This probably isn't a useful loop on its own, but it's a safe way to prove to yourself that you have the ability to handle each file in a directory individually. First, create a simple test environment by creating a directory and placing some copies of some files into it. Any file will do initially, but later examples require graphic files (such as JPEG, PNG, or similar). You can create the folder and copy files into it using a file manager or in the terminal:
|
||||
|
||||
|
||||
```
|
||||
$ mkdir example
|
||||
$ cp ~/Pictures/vacation/*.{png,jpg} example
|
||||
```
|
||||
|
||||
Change directory to your new folder, then list the files in it to confirm that your test environment is what you expect:
|
||||
|
||||
|
||||
```
|
||||
$ cd example
|
||||
$ ls -1
|
||||
cat.jpg
|
||||
design_maori.png
|
||||
otago.jpg
|
||||
waterfall.png
|
||||
```
|
||||
|
||||
The syntax to loop through each file individually in a loop is: create a variable ( **f** for file, for example). Then define the data set you want the variable to cycle through. In this case, cycle through all files in the current directory using the ***** wildcard character (the ***** wildcard matches _everything_ ). Then terminate this introductory clause with a semicolon ( **;** ).
|
||||
|
||||
|
||||
```
|
||||
`$ for f in * ;`
|
||||
```
|
||||
|
||||
Depending on your preference, you can choose to press **Return** here. The shell won't try to execute the loop until it is syntactically complete.
|
||||
|
||||
Next, define what you want to happen with each iteration of the loop. For simplicity, use the **file** command to get a little bit of data about each file, represented by the **f** variable (but prepended with a **$** to tell the shell to swap out the value of the variable for whatever the variable currently contains):
|
||||
|
||||
|
||||
```
|
||||
`do file $f ;`
|
||||
```
|
||||
|
||||
Terminate the clause with another semi-colon and close the loop:
|
||||
|
||||
|
||||
```
|
||||
`done`
|
||||
```
|
||||
|
||||
Press **Return** to start the shell cycling through _everything_ in the current directory. The **for** loop assigns each file, one by one, to the variable **f** and runs your command:
|
||||
|
||||
|
||||
```
|
||||
$ for f in * ; do
|
||||
> file $f ;
|
||||
> done
|
||||
cat.jpg: JPEG image data, EXIF standard 2.2
|
||||
design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
otago.jpg: JPEG image data, EXIF standard 2.2
|
||||
waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
```
|
||||
|
||||
You can also write it this way:
|
||||
|
||||
|
||||
```
|
||||
$ for f in *; do file $f; done
|
||||
cat.jpg: JPEG image data, EXIF standard 2.2
|
||||
design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
otago.jpg: JPEG image data, EXIF standard 2.2
|
||||
waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
```
|
||||
|
||||
Both the multi-line and single-line formats are the same to your shell and produce the exact same results.
|
||||
|
||||
### A practical example
|
||||
|
||||
Here's a practical example of how a loop can be useful for everyday computing. Assume you have a collection of vacation photos you want to send to friends. Your photo files are huge, making them too large to email and inconvenient to upload to your [photo-sharing service][2]. You want to create smaller web-versions of your photos, but you have 100 photos and don't want to spend the time reducing each photo, one by one.
|
||||
|
||||
First, install the **ImageMagick** command using your package manager on Linux, BSD, or Mac. For instance, on Fedora and RHEL:
|
||||
|
||||
|
||||
```
|
||||
`$ sudo dnf install ImageMagick`
|
||||
```
|
||||
|
||||
On Ubuntu or Debian:
|
||||
|
||||
|
||||
```
|
||||
`$ sudo apt install ImageMagick`
|
||||
```
|
||||
|
||||
On BSD, use **ports** or [pkgsrc][3]. On Mac, use [Homebrew][4] or [MacPorts][5].
|
||||
|
||||
Once you install ImageMagick, you have a set of new commands to operate on photos.
|
||||
|
||||
Create a destination directory for the files you're about to create:
|
||||
|
||||
|
||||
```
|
||||
`$ mkdir tmp`
|
||||
```
|
||||
|
||||
To reduce each photo to 33% of its original size, try this loop:
|
||||
|
||||
|
||||
```
|
||||
`$ for f in * ; do convert $f -scale 33% tmp/$f ; done`
|
||||
```
|
||||
|
||||
Then look in the **tmp** folder to see your scaled photos.
|
||||
|
||||
You can use any number of commands within a loop, so if you need to perform complex actions on a batch of files, you can place your whole workflow between the **do** and **done** statements of a **for** loop. For example, suppose you want to copy each processed photo straight to a shared photo directory on your web host and remove the photo file from your local system:
|
||||
|
||||
|
||||
```
|
||||
$ for f in * ; do
|
||||
convert $f -scale 33% tmp/$f
|
||||
scp -i seth_web tmp/$f [seth@example.com][6]:~/public_html
|
||||
trash tmp/$f ;
|
||||
done
|
||||
```
|
||||
|
||||
For each file processed by the **for** loop, your computer automatically runs three commands. This means if you process just 10 photos this way, you save yourself 30 commands and probably at least as many minutes.
|
||||
|
||||
### Limiting your loop
|
||||
|
||||
A loop doesn't always have to look at every file. You might want to process only the JPEG files in your example directory:
|
||||
|
||||
|
||||
```
|
||||
$ for f in *.jpg ; do convert $f -scale 33% tmp/$f ; done
|
||||
$ ls -m tmp
|
||||
cat.jpg, otago.jpg
|
||||
```
|
||||
|
||||
Or, instead of processing files, you may need to repeat an action a specific number of times. A **for** loop's variable is defined by whatever data you provide it, so you can create a loop that iterates over numbers instead of files:
|
||||
|
||||
|
||||
```
|
||||
$ for n in {0..4}; do echo $n ; done
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
```
|
||||
|
||||
### More looping
|
||||
|
||||
You now know enough to create your own loops. Until you're comfortable with looping, use them on _copies_ of the files you want to process and, as often as possible, use commands with built-in safeguards to prevent you from clobbering your data and making irreparable mistakes, like accidentally renaming an entire directory of files to the same name, each overwriting the other.
|
||||
|
||||
For advanced **for** loop topics, read on.
|
||||
|
||||
### Not all shells are Bash
|
||||
|
||||
The **for** keyword is built into the Bash shell. Many similar shells use the same keyword and syntax, but some shells, like [tcsh][7], use a different keyword, like **foreach** , instead.
|
||||
|
||||
In tcsh, the syntax is similar in spirit but more strict than Bash. In the following code sample, do not type the string **foreach?** in lines 2 and 3. It is a secondary prompt alerting you that you are still in the process of building your loop.
|
||||
|
||||
|
||||
```
|
||||
$ foreach f (*)
|
||||
foreach? file $f
|
||||
foreach? end
|
||||
cat.jpg: JPEG image data, EXIF standard 2.2
|
||||
design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
otago.jpg: JPEG image data, EXIF standard 2.2
|
||||
waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
```
|
||||
|
||||
In tcsh, both **foreach** and **end** must appear alone on separate lines, so you cannot create a **for** loop on one line as you can with Bash and similar shells.
|
||||
|
||||
### For loops with the find command
|
||||
|
||||
In theory, you could find a shell that doesn't provide a **for** loop function, or you may just prefer to use a different command with added features.
|
||||
|
||||
The **find** command is another way to implement the functionality of a **for** loop, as it offers several ways to define the scope of which files to include in your loop as well as options for [Parallel][8] processing.
|
||||
|
||||
The **find** command is meant to help you find files on your hard drives. Its syntax is simple: you provide the path of the location you want to search, and **find** finds all files and directories:
|
||||
|
||||
|
||||
```
|
||||
$ find .
|
||||
.
|
||||
./cat.jpg
|
||||
./design_maori.png
|
||||
./otago.jpg
|
||||
./waterfall.png
|
||||
```
|
||||
|
||||
You can filter the search results by adding some portion of the name:
|
||||
|
||||
|
||||
```
|
||||
$ find . -name "*jpg"
|
||||
./cat.jpg
|
||||
./otago.jpg
|
||||
```
|
||||
|
||||
The great thing about **find** is that each file it finds can be fed into a loop using the **-exec** flag. For instance, to scale down only the PNG photos in your example directory:
|
||||
|
||||
|
||||
```
|
||||
$ find . -name "*png" -exec convert {} -scale 33% tmp/{} \;
|
||||
$ ls -m tmp
|
||||
design_maori.png, waterfall.png
|
||||
```
|
||||
|
||||
In the **-exec** clause, the bracket characters **{}** stand in for whatever item **find** is processing (in other words, any file ending in PNG that has been located, one at a time). The **-exec** clause must be terminated with a semicolon, but Bash usually tries to use the semicolon for itself. You "escape" the semicolon with a backslash ( **\;** ) so that **find** knows to treat that semicolon as its terminating character.
|
||||
|
||||
The **find** command is very good at what it does, and it can be too good sometimes. For instance, if you reuse it to find PNG files for another photo process, you will get a few errors:
|
||||
|
||||
|
||||
```
|
||||
$ find . -name "*png" -exec convert {} -flip -flop tmp/{} \;
|
||||
convert: unable to open image `tmp/./tmp/design_maori.png':
|
||||
No such file or directory @ error/blob.c/OpenBlob/2643.
|
||||
...
|
||||
```
|
||||
|
||||
It seems that **find** has located all the PNG files—not only the ones in your current directory ( **.** ) but also those that you processed before and placed in your **tmp** subdirectory. In some cases, you may want **find** to search the current directory plus all other directories within it (and all directories in _those_ ). It can be a powerful recursive processing tool, especially in complex file structures (like directories of music artists containing directories of albums filled with music files), but you can limit this with the **-maxdepth** option.
|
||||
|
||||
To find only PNG files in the current directory (excluding subdirectories):
|
||||
|
||||
|
||||
```
|
||||
`$ find . -maxdepth 1 -name "*png"`
|
||||
```
|
||||
|
||||
To find and process files in the current directory plus an additional level of subdirectories, increment the maximum depth by 1:
|
||||
|
||||
|
||||
```
|
||||
`$ find . -maxdepth 2 -name "*png"`
|
||||
```
|
||||
|
||||
Its default is to descend into all subdirectories.
|
||||
|
||||
### Looping for fun and profit
|
||||
|
||||
The more you use loops, the more time and effort you save, and the bigger the tasks you can tackle. You're just one user, but with a well-thought-out loop, you can make your computer do the hard work.
|
||||
|
||||
You can and should treat looping like any other command, keeping it close at hand for when you need to repeat a single action or two on several files. However, it's also a legitimate gateway to serious programming, so if you have to accomplish a complex task on any number of files, take a moment out of your day to plan out your workflow. If you can achieve your goal on one file, then wrapping that repeatable process in a **for** loop is relatively simple, and the only "programming" required is an understanding of how variables work and enough organization to separate unprocessed from processed files. With a little practice, you can move from a Linux user to a Linux user who knows how to write a loop, so get out there and make your computer work for you!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/6/how-write-loop-bash
|
||||
|
||||
作者:[Seth Kenlon][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/seth/users/goncasousa/users/howtopamm/users/howtopamm/users/seth/users/wavesailor/users/seth
|
||||
[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]: http://nextcloud.com
|
||||
[3]: http://pkgsrc.org
|
||||
[4]: http://brew.sh
|
||||
[5]: https://www.macports.org
|
||||
[6]: mailto:seth@example.com
|
||||
[7]: https://en.wikipedia.org/wiki/Tcsh
|
||||
[8]: https://opensource.com/article/18/5/gnu-parallel
|
260
translated/tech/20190612 How to write a loop in Bash.md
Normal file
260
translated/tech/20190612 How to write a loop in Bash.md
Normal file
@ -0,0 +1,260 @@
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "chunibyo-wly"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
[#]: subject: "How to write a loop in Bash"
|
||||
[#]: via: "https://opensource.com/article/19/6/how-write-loop-bash"
|
||||
[#]: author: "Seth Kenlon https://opensource.com/users/seth/users/goncasousa/users/howtopamm/users/howtopamm/users/seth/users/wavesailor/users/seth"
|
||||
|
||||
# 如何在 Bash 中使用循环
|
||||
|
||||
使用循环和查找命令批量自动处理文件。
|
||||
![bash logo on green background][1]
|
||||
|
||||
人们希望学习批处理命令的一个普遍原因是批处理具有强大的功能。如果你希望批量的对文件执行指令,制作一个可以重复运行在那些文件上的命令就是一种方法。在编程术语中,这被称作 _execution control_ ,**for** 循环就是其中最常见的一种。
|
||||
|
||||
**for** 循环可以详细描述你希望你的计算机对 _for_ 遍历的数据对象(比如说文件)所进行的操作。
|
||||
|
||||
### 一般循环
|
||||
|
||||
使用循环的一个简单例子是对一组文件进行分析。这个循环可能没什么用,但是这是一个安全的证明自己有能力独立处理文件夹里每一个文件的方法。首先,创建一个文件夹然后拷贝一些文件(例如 JPEG,PNG 等类似的文件)至文件夹中生成一个测试环境。你可以通过文件管理器或者终端来完成创建文件夹和拷贝文件的操作:
|
||||
|
||||
```bash
|
||||
$ mkdir example
|
||||
$ cp ~/Pictures/vacation/*.{png,jpg} example
|
||||
```
|
||||
|
||||
切换到你刚创建的那个文件夹,然后列出文件并确认这个测试环境是你需要的:
|
||||
|
||||
```
|
||||
$ cd example
|
||||
$ ls -1
|
||||
cat.jpg
|
||||
design_maori.png
|
||||
otago.jpg
|
||||
waterfall.png
|
||||
```
|
||||
|
||||
在循环中逐一遍历文件的语法是:首先声明一个变量(例如使用 **f** 代表文件),然后定义一个你希望用变量循环的数据集。在这种情况下,使用 **\*** 通配符来遍历当前文件夹下的所有文件(通配符 **\*** 匹配 _一切_)。然后使用一个分号(**;**)来结束这个语句。
|
||||
|
||||
```
|
||||
`$ for f in * ;`
|
||||
```
|
||||
|
||||
取决于你个人的喜好,你可以选择在这里按下 **Return**。在语法完成前,shell 是不会尝试执行这个循环的。
|
||||
|
||||
接下来,定义在每次循环中你想要进行的操作。简单起见,使用 **file** 命令来得到 **f** 变量(使用 **\$** 告诉 shell 使用这个变量的值,无论这个变量现在存储着什么)所存储着的文件的信息:
|
||||
|
||||
```
|
||||
`do file $f ;`
|
||||
```
|
||||
|
||||
使用另一个引号结束这一行,然后关闭这个循环:
|
||||
|
||||
```
|
||||
`done`
|
||||
```
|
||||
|
||||
按下 **Return** 启动 shell 对当前文件夹下 _所有东西_ 的遍历。**for** 循环将会一个一个的将文件分配给变量 **f** 并且执行你的命令:
|
||||
|
||||
```
|
||||
$ for f in * ; do
|
||||
> file $f ;
|
||||
> done
|
||||
cat.jpg: JPEG image data, EXIF standard 2.2
|
||||
design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
otago.jpg: JPEG image data, EXIF standard 2.2
|
||||
waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
```
|
||||
|
||||
你也可以用这种形式书写命令:
|
||||
|
||||
```
|
||||
$ for f in *; do file $f; done
|
||||
cat.jpg: JPEG image data, EXIF standard 2.2
|
||||
design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
otago.jpg: JPEG image data, EXIF standard 2.2
|
||||
waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
```
|
||||
|
||||
对你的 shell 来说,多行和单行的格式没有什么区别,并且会输出完全一样的结果。
|
||||
|
||||
### 一个实用的例子
|
||||
|
||||
下面是一个循环在日常使用中的实用案例。假如你拥有一堆假期拍的照片想要发给你的朋友。它们对于 email 来说太大了,上传到 [photo-sharing service][2] 也不方便。因此你希望创建小型的 web 版本的照片,但是你不希望花费太多时间在一个一个的压缩图片体积上。
|
||||
|
||||
首先,在你的 Linux,BSD 或者 Mac 上使用包管理器安装 **ImageMagick** 命令。例如,在 Fedora 和 RHEL 上:
|
||||
|
||||
```
|
||||
`$ sudo dnf install ImageMagick`
|
||||
```
|
||||
|
||||
在 Ubuntu 和 Debian 上:
|
||||
|
||||
```
|
||||
`$ sudo apt install ImageMagick`
|
||||
```
|
||||
|
||||
在 BSD 上,使用 **ports** 或者 [pkgsrc][3]。在 Mac 上,使用 [Homebrew][4] 或者 [MacPorts][5]。
|
||||
|
||||
在你安装了 ImageMagick 之后,你就拥有一系列命令可以用来操作图片了。
|
||||
|
||||
为你将要创建的文件建立一个目标文件夹:
|
||||
|
||||
```
|
||||
`$ mkdir tmp`
|
||||
```
|
||||
|
||||
使用下面的循环可以将每张图片减小至原来大小的 33%。
|
||||
|
||||
```
|
||||
`$ for f in * ; do convert $f -scale 33% tmp/$f ; done`
|
||||
```
|
||||
|
||||
然后就可以在 **tmp** 文件夹中看到已经缩小了的照片了。
|
||||
|
||||
你可以在循环体中使用任意数量的命令,因此如果你需要对一批文件进行复杂的操作,可以将你的命令放在一个 **for** 循环的 **do** 和 **done** 声明之间。例如,假设你希望将所有处理过的图片拷贝至你的网站所托管的图片文件夹并且在本地系统移除这些文件:
|
||||
|
||||
```
|
||||
$ for f in * ; do
|
||||
convert $f -scale 33% tmp/$f
|
||||
scp -i seth_web tmp/$f [seth@example.com][6]:~/public_html
|
||||
trash tmp/$f ;
|
||||
done
|
||||
```
|
||||
|
||||
你的计算机会对 **for** 循环中处理的每一个文件自动的执行 3 条命令。这意味着假如你仅仅处理 10 张图片,也会省下 30 条指令和更多的时间。
|
||||
|
||||
### 限制你的循环
|
||||
|
||||
一个循环常常不需要处理所有文件。在示例文件夹中,你可能需要处理的只是 JPEG 文件:
|
||||
|
||||
```
|
||||
$ for f in *.jpg ; do convert $f -scale 33% tmp/$f ; done
|
||||
$ ls -m tmp
|
||||
cat.jpg, otago.jpg
|
||||
```
|
||||
|
||||
或者,你希望重复特定次数的某个操作而不仅仅只处理文件。**for** 循环的变量的值是被你赋给它的不管何种类型的数据所决定的,所以你可以创建一个循环遍历数字而不只是文件:
|
||||
|
||||
```
|
||||
$ for n in {0..4}; do echo $n ; done
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
```
|
||||
|
||||
### 更多循环
|
||||
|
||||
现在你了解的知识已经足够用来创建自己的循环体了。直到你对循环非常熟悉之前,尽可能的在需要处理的文件的 _复制_ 上进行操作。使用固有的保护措施可以预防损坏自己的数据和制造不可复现的错误,例如偶然将一个文件夹下的所有文件重命名为同一个名字,就可能会导致他们的相互覆盖。
|
||||
|
||||
更多的 **for** 循环话题,请继续阅读。
|
||||
|
||||
### Not all shells are Bash 不是所有的 shell 都是 Bash
|
||||
|
||||
关键字 **for** 是内置在 Bash shell 中的。许多类似的 shell 会使用和 Bash 同样的关键字和语法,但是也有某些 shell ,比如 [tcsh][7],使用不同的关键字,例如 **foreach**。
|
||||
|
||||
tcsh 的语法与 Bash 类似,但是它更为严格。例如在下面的例子中,不要在你的终端的第 2,3 行键入 **foreach?** 。它只是提示你仍处在构建循环的过程中。
|
||||
|
||||
```
|
||||
$ foreach f (*)
|
||||
foreach? file $f
|
||||
foreach? end
|
||||
cat.jpg: JPEG image data, EXIF standard 2.2
|
||||
design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
otago.jpg: JPEG image data, EXIF standard 2.2
|
||||
waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
|
||||
```
|
||||
|
||||
在 tcsh 中,**foreach** 和 **end** 都必须单独的在一行中出现。因此你不能像 Bash 或者其他类似的 shell 一样只使用一行命令创建一个 **for** 循环。
|
||||
|
||||
### for 循环与 find 命令
|
||||
|
||||
理论上,你可能会用到不支持 **for** 循环的 shell,或者你只是更想使用其他命令的一些特性来完成和循环一样的工作。
|
||||
|
||||
使用**find** 命令是另一个实现 **for** 循环功能的途径。这个命令通过 [Parallel][8] 提供了几种方法来定义你的循环中需要包括的文件的范围。
|
||||
|
||||
**find** 命令顾名思义就是帮助你查询存储在硬盘里的文件。他的用法很简单:提供一个你希望它查询的位置的路径,接着 **find** 就会查询这个路径下面的所有文件和文件夹。
|
||||
|
||||
```
|
||||
$ find .
|
||||
.
|
||||
./cat.jpg
|
||||
./design_maori.png
|
||||
./otago.jpg
|
||||
./waterfall.png
|
||||
```
|
||||
|
||||
你可以通过添加名称的某些部分用于过滤搜索结果:
|
||||
|
||||
```
|
||||
$ find . -name "*jpg"
|
||||
./cat.jpg
|
||||
./otago.jpg
|
||||
```
|
||||
|
||||
**find** 命令非常好的地方在于你可以通过 **-exec** 参数标志将它查询到的每一个文件放入循环中。例如,只对存放在你的 example 文件夹下的 PNG 图片进行体积压缩操作:
|
||||
|
||||
```
|
||||
$ find . -name "*png" -exec convert {} -scale 33% tmp/{} \;
|
||||
$ ls -m tmp
|
||||
design_maori.png, waterfall.png
|
||||
```
|
||||
|
||||
在 **-exec** 短语中,括号 **{}** 表示的是 **find** 正在处理的条目(换句话说,每一个被找到的以 PNG 结尾的文件)。**-exec** 短语必须使用分号结尾,但是 Bash 中常常也会使用分号。为了解决这个二义性问题,你的 **结束符** 可以使用反斜杠加上一个分号(**\;**),使得 **find** 命令可以知道这个结束符是用来标识自己结束使用的。
|
||||
|
||||
**find** 命令的操作非常棒,某些情况下它甚至可以表现得更棒。比如说,在一个新的进程中使用同一条命令查找 PNG 文件,你可能就会得到一些错误信息:
|
||||
|
||||
```
|
||||
$ find . -name "*png" -exec convert {} -flip -flop tmp/{} \;
|
||||
convert: unable to open image `tmp/./tmp/design_maori.png':
|
||||
No such file or directory @ error/blob.c/OpenBlob/2643.
|
||||
...
|
||||
```
|
||||
|
||||
看起来 **find** 不只是定位了当前文件夹(**.**)下的所有 PNG 文件,还包括已经处理并且存储到了 **tmp** 下的文件。在一些情况下,你可能希望 **find** 查询当前文件夹下再加上其子文件夹下的所有文件。**find** 命令是一个功能强大的递归工具,特别体现在处理一些文件结构复杂的情境下(比如用来放置存满了音乐人音乐专辑的文件夹),同时你也可以使用 **-maxdepth** 选项来限制最大的递归深度。
|
||||
|
||||
只在当前文件夹下查找 PNG 文件(不包括子文件夹)
|
||||
|
||||
```
|
||||
`$ find . -maxdepth 1 -name "*png"`
|
||||
```
|
||||
|
||||
上一条命令的最大深度再加 1 就可以查找和处理当前文件夹及下一级子文件夹下面的文件
|
||||
|
||||
```
|
||||
`$ find . -maxdepth 2 -name "*png"`
|
||||
```
|
||||
|
||||
**find** 命令默认是查找每一级文件夹。
|
||||
|
||||
### 循环的乐趣与收益
|
||||
|
||||
你使用的循环越多,你就可以越多的省下时间和力气,并且可以应对庞大的任务。虽然你只是一个用户,但是通过使用循环,可以使你的计算机完成困难的任务。
|
||||
|
||||
你可以并且应该使用循环就像使用其他的命令一样。在你需要重复的处理单个或多个文件时,尽可能的使用这个命令。无论如何,这也算是一项需要被严肃对待的编程活动,因此如果你需要在一些文件上完成复杂的任务,你应该多花点时间在计划自己的工作流上面。如果你可以在一份文件上完成你的工作,接下来将操作包装进 **for** 循环里就相对简单了,这里面唯一的“编程”的需要只是理解变量是如何工作的并且进行充分的规划工作将已处理过的文件和未处理过的文件分开。经过一段时间的练习,你就可以从一名 Linux 用户升级成一位知道如何使用循环的 Linux 用户,所以开始让计算机为你工作吧!
|
||||
|
||||
---
|
||||
|
||||
via: https://opensource.com/article/19/6/how-write-loop-bash
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[chunibyo-wly](https://github.com/chunibyo-wly)
|
||||
校对:[校对者 ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux 中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth/users/goncasousa/users/howtopamm/users/howtopamm/users/seth/users/wavesailor/users/seth
|
||||
[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]: http://nextcloud.com
|
||||
[3]: http://pkgsrc.org
|
||||
[4]: http://brew.sh
|
||||
[5]: https://www.macports.org
|
||||
[6]: mailto:seth@example.com
|
||||
[7]: https://en.wikipedia.org/wiki/Tcsh
|
||||
[8]: https://opensource.com/article/18/5/gnu-parallel
|
155
translated/tech/20200331 5 ways to level up your Vim skills.md
Normal file
155
translated/tech/20200331 5 ways to level up your Vim skills.md
Normal file
@ -0,0 +1,155 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (5 ways to level up your Vim skills)
|
||||
[#]: via: (https://opensource.com/article/20/3/vim-skills)
|
||||
[#]: author: (Detlef Johnson https://opensource.com/users/deckart)
|
||||
|
||||
提升你的 Vim 技能的 5 个方法
|
||||
======
|
||||
|
||||
> 通过一些有趣的技巧使你的文本编辑器更上一层楼。
|
||||
|
||||
![Computer keyboard typing][1]
|
||||
|
||||
Vim 是最受欢迎的文本编辑器之一,因此绝对值得花时间学习如何使用它。如果你使用这个无处不在的 [Vi(m)][2] 命令行文本编辑器来做的只是打开文件、输入和编辑一些文本、保存编辑的文件并退出程序,那么你还有很大的提示空间、。
|
||||
|
||||
在某些情况下你会发现,使用 Vim 非常方便的的场景几乎总是涉及到运行远程 Shell 操作的任务。如果你时不时地使用 ssh,比如
|
||||
|
||||
```
|
||||
$ ssh user@hostname.provider.com
|
||||
```
|
||||
|
||||
并在虚拟专用服务器(VPS)或本地虚拟化容器中工作,那么就可以从这些强大的 Vim 技能中受益匪浅。
|
||||
|
||||
### 将 Vim 设置为默认的文本编辑器
|
||||
|
||||
几乎在所有现代 Linux(或 BSD)发行版中,都可以在终端仿真器的 shell 命令提示符下使用 Vim。在用户 shell 程序中将 Vim 定义为默认编辑器后,即可使用熟悉的 Vim 键绑定来浏览内置的实用程序,例如 `man`。我将说明如何使用 Bash 和 Z shell(zsh)来实现此目的,zsh 现在是 macOS 用户的默认shell(自 Catalina 起)。
|
||||
|
||||
#### 在 Bash 中将 Vim 设置为默认
|
||||
|
||||
Bash 通过点文件的组合来管理设置。将首选编辑器添加到主目录中的 `.bashrc` 文件中是最常见的,但也可以将其添加到 `.bash_profile` 中。(请阅读 [GNU Bash 文档][3]了解不同之处)。
|
||||
|
||||
通过在 `~/.bashrc` 中添加以下内容,将 Vim 设置为默认编辑器:
|
||||
|
||||
```
|
||||
# set default editor to Vim
|
||||
export EDITOR=vim
|
||||
```
|
||||
|
||||
以 `#` 开头的行是可选的注释,这是提醒自己该命令的功能的好方法。
|
||||
|
||||
#### 在 zsh 中将 Vim 设置为默认
|
||||
|
||||
zsh 是一种越来越流行的终端模拟器,尤其是自苹果的基于 FreeBSD 的 Darwin 系统最近从 Bash 切换到 zsh 以来。
|
||||
|
||||
zsh 点文件与 Bash 的文件相当,因此你可以在 `~/.zshrc` 或 `~/.zprofile` 之间进行选择。有关何时使用哪一个的详细信息,请参见 [zsh文档][4]。
|
||||
|
||||
将其设置为默认:
|
||||
|
||||
```
|
||||
# set default editor to Vim
|
||||
export EDITOR=vim
|
||||
```
|
||||
|
||||
### 优化 Vim 配置
|
||||
|
||||
Vim 很像终端仿真器外壳,它使用点文件来设置个人偏好。如果发现该模式,则可能已经猜到它是 `~/.vimrc`。
|
||||
|
||||
你可能要更改的第一个设置是将对旧 Vi 兼容模式切换为“关”。由于 Vim 是 Vi 的超集,因此 Vi 中的所有功能都可用,并在 Vim 中进行了很大的改进,你可以获得许多高级功能。最新版本(8.2)允许你在拆分的窗口中打开终端运行一个子进程 shell 程序。
|
||||
|
||||
顺便说一句,关闭旧版兼容性似乎没有做什么事情([事实上,可能不是][5])。当遇到一个 `.vimrc` 文件时,Vim 会自动将该模式切换为关闭。但有时将其明确关闭仍然很重要。缩写 `nocp` 是 `nocompatible` 的同义词,也可以使用它。[条条大道通罗马][6],切换首选项有很多方式。
|
||||
|
||||
在 `.vimrc` 语法中, 以 `"` 开头的行是注释(就像 `.bashrc` 文件中的 `#` 一样),这些注释可以帮助你记住诸如为何选择一个隐秘的设置名称之类的内容。
|
||||
|
||||
要关闭 Vi 兼容性,请将以下内容添加到 `~/.vimrc` 文件中:
|
||||
|
||||
```
|
||||
" ensure that legacy compatibility mode is off
|
||||
" documentation: <http://vimdoc.sourceforge.net/htmldoc/options.html\#'compatible>'
|
||||
set nocp
|
||||
```
|
||||
|
||||
### 理解模式
|
||||
|
||||
Vim的 “模式”概念是非常重要的,尤其是“正常模式”和“插入模式”之间的区别。对模式的混淆是大多数新用户的困扰。模式并不是 Vim 所独有的,甚至也不是 Vi 所引入的。命令模式是如此的古老,以至于它比 70 年代的[复制和粘贴][7]功能的发明还要早。
|
||||
|
||||
#### 重要的模式
|
||||
|
||||
Vim 依赖于不同的模式来定义键盘的敲击行为。需要了解的重要模式有
|
||||
|
||||
* 正常模式:默认模式,主要用于导航和打开文件。
|
||||
* 插入模式(包括替换):这种模式下 Vim 允许将文本输入到打开的文件中。
|
||||
* 可视模式:Vim 的行为类似于基于鼠标的输入方式,如复制、编辑、替换等。
|
||||
* 命令模式(包括行模式、Ex 命令模式和末行模式):在 Vim 中做更多事情的强大方法。
|
||||
|
||||
每种模式都有很多值得探索的地方。使用 [Vimtutor][8](`vimtutor`)可以交互式地学习移动光标、模式和在末行模式下运行 Ex 命令。一些不可缺少的生产力操作符包括:
|
||||
|
||||
`:E` | 打开资源管理器,用于定位文件和目录。
|
||||
--- | ---
|
||||
`.` | 重复上次的编辑操作。
|
||||
`;` | 向前重复上一次的动作或移动
|
||||
`,` | 向后重复上一次的动作或移动。
|
||||
`/` | 向前搜索文档。
|
||||
`?` | 向后搜索文档。
|
||||
`*` | 查找光标所在处的单词的下一个出现的地方。
|
||||
`#` | 查找光标所在处的单词的上一次出现的地方。
|
||||
`~` | 切换大小写。
|
||||
`%` | 在打开和关闭的 `()`、`[]` 和 `{}` 之间切换;对编码非常有用。
|
||||
`z=` | 提出拼写建议。
|
||||
|
||||
### 像钢琴一样弹奏 Vim
|
||||
|
||||
把 Vim 的操作符“语言”记在记忆中是很重要的,但要想掌握它,难点在于学会像音乐家一样思考,把操作符和动作组合成“和声和弦”,这样你就可以像弹钢琴一样弹奏 Vim。这就是 Vim 的文本操作能力可以与另一个著名的命令行编辑器 Emacs 相媲美的地方。(虽然其中一个编辑器会让磨损掉你的 `Esc` 键,而另一个编辑器会让你的 `Ctrl` 键磨损掉。)
|
||||
|
||||
在描述和弦时,Vim 中的传统做法是用大写字母 `C` 来指代 `Ctrl` 键,后面加上一个连字符(`C-`)。这并不是通用的,但我将从这里开始遵循这一惯例,并在有可能引起混淆的时候加以说明。
|
||||
|
||||
如果你在 Vim 中键入长行,你会想把它设置成可以换行你的文字。想要根据你的工作方式对 Vim 进行个性化设置,请考虑一下这个设置:当 Vim 启动时,你希望 Vim 默认情况下如何处理文本换行?开着还是关着?我喜欢将其关闭,并在运行时用命令打开它。当我想让文本换行时,我只需在命令行模式下用 `:set wrap` 设置即可。
|
||||
|
||||
让 Vim 设置为默认文字换行并没有什么问题。这只是一个偏好的问题 —— 它可能随着时间的推移而改变。同样你也可以控制粘贴、代码语言缩进语法和 `Tab` 键的设置(制表符还是空格?多少个空格?可也在[这里][9]深入研究这些选项)。所有这些默认行为的选项都是完全可配置的,并且在你使用命令行模式操作时可以实时更改。
|
||||
|
||||
你会在社区论坛、Vim 维基和文章中找到很多关于设置 Vim 默认设置的建议(比如这篇文章)。为你的个人计算环境设置首选项对你来说应该相当熟悉,Vim 也不例外。我强烈建议你从对你的设置进行非常小的更改开始,慢慢地进行更多的更改,这样你就可以轻松地恢复设置。这样一来,你就可以避免使用插件好多年或完全不用。
|
||||
|
||||
### Vim 8.2中的分割、标签和终端
|
||||
|
||||
有两种方法可以将你正在处理的文件分割成不同的视图:它们可以并排显示,也可以使用应用程序标签页在全屏(窗口)中切换。这些对应用程序窗口的更改是从命令模式启动的,这需要使用冒号(`:`)来调起提示符。
|
||||
|
||||
每个分割的窗口可以容纳一个文件进行编辑,你可以通过标签页在更多的文件之间随意切换。分割的屏幕空间是有限的,所以当你想分割更多的屏幕时,标签页是很方便的。想要如何设置,纯属个人喜好的问题。要横向分割一个窗口,使用 `:sp`,垂直分割时使用 `:vs`。
|
||||
|
||||
从 [Vim 8.2][10] 开始,你可以用 `:vert term` 打开一个垂直分割的终端 shell 子进程,来在你的代码旁边在命令行进行操作。你需要键入 `exit` 来关闭你的终端进程,就像你结束一个 shell 会话一样,但你关闭这个分割的窗口和标签页的方式和关闭任何普通的 Vim 窗口一样,用 `:q` 来关闭。
|
||||
|
||||
要初始化一个标签页,请使用一个特殊的编辑命令:`:tabedit`,它会自动切换到新打开的标签页。如果你给该命令一个文件名作为参数,将会打开该文件并进行编辑。如果你忽略了给它一个文件名作为参数,可以在命令行模式下的使用编辑命令 `:e filename.txt`,就像在任何一个普通的 Vim 窗口中一样。可以使用下一个(`:tabn`)和上一个(`:tabp`)命令在标签页间导航。
|
||||
|
||||
要使用分割,你需要知道如何使用组合键 `C-w` 和你想要移动的方向的移动键,例如左(`h`)、下(`j`)、左(`k`)、右(`l`)。如果你想学习更多的组合键,请阅读 Vim 手册中的 `:help split` 和 `:help tabpage`。
|
||||
|
||||
### 获取帮助
|
||||
|
||||
虽然可以在 Vimtutor 中打开参考 Vim 手册,但用 `:help` 打开 Vim 帮助,可以让你自己把时间花在编辑器上,不用完全依赖像这样的文章,就能获得更多的成果。经验是掌握 Vim 的关键。经验有助于提高你的整体计算直觉,因为 Vim 中的很多东西都是从 Unix 宇宙中汲取的。
|
||||
|
||||
祝你在探索 Vim 之美的过程中玩得开心,有什么问题可以在评论中分享。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/vim-skills
|
||||
|
||||
作者:[Detlef Johnson][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/deckart
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/keyboaord_enter_writing_documentation.jpg?itok=kKrnXc5h (Computer keyboard typing)
|
||||
[2]: https://www.vim.org/
|
||||
[3]: https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html
|
||||
[4]: http://zsh.sourceforge.net/Intro/intro_3.html
|
||||
[5]: http://vimdoc.sourceforge.net/htmldoc/starting.html#compatible-default
|
||||
[6]: https://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it
|
||||
[7]: https://www.npr.org/2020/02/22/808404858/remembering-the-pioneer-behind-your-computers-cut-copy-and-paste-functions
|
||||
[8]: http://www2.geog.ucl.ac.uk/~plewis/teaching/unix/vimtutor
|
||||
[9]: https://opensource.com/article/18/9/vi-editor-productivity-powerhouse
|
||||
[10]: https://www.vim.org/vim-8.2-released.php
|
Loading…
Reference in New Issue
Block a user