190 lines
2.8 KiB
Plaintext
190 lines
2.8 KiB
Plaintext
# File handling
|
|
|
|
**CAVEAT:** `bash` doesn't handle binary data properly in versions `< 4.4`.
|
|
|
|
## Read a file to a string
|
|
|
|
Alternative to the `cat` command.
|
|
|
|
```shell
|
|
file_data="$(<"file")"
|
|
```
|
|
|
|
## Read a file to an array (*by line*)
|
|
|
|
Alternative to the `cat` command.
|
|
|
|
```shell
|
|
# Bash <4
|
|
IFS=$'\n' read -d "" -ra file_data < "file"
|
|
|
|
# Bash 4+
|
|
mapfile -t file_data < "file"
|
|
```
|
|
|
|
## Get the first N lines of a file
|
|
|
|
Alternative to the `head` command.
|
|
|
|
**CAVEAT:** Requires `bash` 4+
|
|
|
|
**Example Function:**
|
|
|
|
```sh
|
|
head() {
|
|
# Usage: head "n" "file"
|
|
mapfile -tn "$1" line < "$2"
|
|
printf '%s\n' "${line[@]}"
|
|
}
|
|
```
|
|
|
|
**Example Usage:**
|
|
|
|
```shell
|
|
$ head 2 ~/.bashrc
|
|
# Prompt
|
|
PS1='➜ '
|
|
|
|
$ head 1 ~/.bashrc
|
|
# Prompt
|
|
```
|
|
|
|
## Get the last N lines of a file
|
|
|
|
Alternative to the `tail` command.
|
|
|
|
**CAVEAT:** Requires `bash` 4+
|
|
|
|
**Example Function:**
|
|
|
|
```sh
|
|
tail() {
|
|
# Usage: tail "n" "file"
|
|
mapfile -tn 0 line < "$2"
|
|
printf '%s\n' "${line[@]: -$1}"
|
|
}
|
|
```
|
|
|
|
**Example Usage:**
|
|
|
|
```shell
|
|
$ tail 2 ~/.bashrc
|
|
# Enable tmux.
|
|
# [[ -z "$TMUX" ]] && exec tmux
|
|
|
|
$ tail 1 ~/.bashrc
|
|
# [[ -z "$TMUX" ]] && exec tmux
|
|
```
|
|
|
|
## Get the number of lines in a file
|
|
|
|
Alternative to `wc -l`.
|
|
|
|
**Example Function (bash 4):**
|
|
|
|
```sh
|
|
lines() {
|
|
# Usage: lines "file"
|
|
mapfile -tn 0 lines < "$1"
|
|
printf '%s\n' "${#lines[@]}"
|
|
}
|
|
```
|
|
|
|
**Example Function (bash 3):**
|
|
|
|
This method uses less memory than the `mapfile` method and it's more
|
|
compatible but it's slower for bigger files.
|
|
|
|
```sh
|
|
lines_loop() {
|
|
# Usage: lines_loop "file"
|
|
count=0
|
|
while IFS= read -r _; do
|
|
((count++))
|
|
done < "$1"
|
|
printf '%s\n' "$count"
|
|
}
|
|
```
|
|
|
|
**Example Usage:**
|
|
|
|
```shell
|
|
$ lines ~/.bashrc
|
|
48
|
|
|
|
$ lines_loop ~/.bashrc
|
|
48
|
|
```
|
|
|
|
## Count files or directories in directory
|
|
|
|
This works by passing the output of the glob as function arguments. We
|
|
then count the arguments and print the number.
|
|
|
|
**Example Function:**
|
|
|
|
```sh
|
|
count() {
|
|
# Usage: count /path/to/dir/*
|
|
# count /path/to/dir/*/
|
|
printf '%s\n' "$#"
|
|
}
|
|
```
|
|
|
|
**Example Usage:**
|
|
|
|
```shell
|
|
# Count all files in dir.
|
|
$ count ~/Downloads/*
|
|
232
|
|
|
|
# Count all dirs in dir.
|
|
$ count ~/Downloads/*/
|
|
45
|
|
|
|
# Count all jpg files in dir.
|
|
$ count ~/Pictures/*.jpg
|
|
64
|
|
```
|
|
|
|
## Create an empty file
|
|
|
|
Alternative to `touch`.
|
|
|
|
```shell
|
|
# Shortest.
|
|
:> file
|
|
|
|
# Longer alternatives:
|
|
echo -n > file
|
|
printf '' > file
|
|
```
|
|
|
|
## Extract lines between two markers
|
|
|
|
**Example Function:**
|
|
|
|
```sh
|
|
extract() {
|
|
# Usage: extract file "opening marker" "closing marker"
|
|
while IFS=$'\n' read -r line; do
|
|
[[ "$extract" && "$line" != "$3" ]] && \
|
|
printf '%s\n' "$line"
|
|
|
|
[[ "$line" == "$2" ]] && extract=1
|
|
[[ "$line" == "$3" ]] && extract=
|
|
done < "$1"
|
|
}
|
|
```
|
|
|
|
**Example Usage:**
|
|
|
|
```shell
|
|
# Extract code blocks from MarkDown file.
|
|
$ extract ~/projects/pure-bash/README.md '```sh' '```'
|
|
# Output here...
|
|
```
|
|
|
|
<!-- CHAPTER END -->
|
|
|