Bash

如何在 bash 中倒帶文件描述符而不使用第二個文件描述符從 bash for Mac 的開頭讀取?

  • February 21, 2022

環境: GNU bash,版本 3.2.57(1)-release (x86_64-apple-darwin20)

嘗試0:

exec 3<> "$(mktemp)"    # open file descriptor 3 to a temp file for read/write
echo 'foo' >&3          # write 'foo' to descriptor 3
read bar <&3            # read contents of descriptor 3 to variable named bar (not using -u 3)
echo $?                 # exit status returns "1"
exec 3>&-               # close file descriptor 3

在上面的程式碼片段中,我希望$bar設置為"foo"但它不起作用,因為文件描述符 3 中的位置位於文件描述符文件的末尾。

嘗試1:

exec 3<> "$(mktemp)"    # open file descriptor 3 to a temp file for read/write
echo 'foo' >&3          # write 'foo' to descriptor 3
cat <&3                 # echo contents of of descriptor 3 to STDOUT
echo $?                 # exit status returns "0"
exec 3>&-               # close file descriptor 3

這也失敗了,因為描述符 3 中的文件位置位於末尾。

在 C/C++ 中,您可以rewind(…) 和 fseek(…)將創建的文件臨時文件的文件指針重置到文件mktemp的開頭。

**此處已知的解決方法:(**使用第二個描述符)

myTempFile="$(mktemp)"  # create temp file and assign to $myTempFile
exec 3> "$myTempFile"   # open file descriptor 3 for writing to $myTempFile
exec 4< "$myTempFile"   # open file descriptor 4 for reading of $myTempFile
echo 'foo' >&3          # write 'foo' to descriptor 3
read bar <&4            # read contents of descriptor 4 to variable named bar
echo $?                 # exit status returns "0"
echo "$bar"             # echo "foo"
exec 3>&-               # close file descriptor 3
exec 4>&-               # close file descriptor 4

問題™(最終):

如何在 bash 中回退文件描述符而不使用第二個文件描述符從頭開始讀取?

注意:我不想安裝ksh93來使用它的倒帶功能。

exec 3<> "$(mktemp)"
echo 'foo' >&3
# Magic Happens here
read bar <&3             # expect $bar == "foo"
echo $?                  # expect "0"
exec 3>&- 

雖然這是一個有趣的問題(但也是我 30 年來從未需要用外殼做的事情)。您可能正在嘗試使用錯誤的工具。

Unix(UNIX、BSD、MacOS、Gnu/Linux)有管道。管道是一個特殊的文件(不在二級儲存/磁碟中),它有兩個文件描述符。一種用於寫作,一種用於閱讀。讀描述符總是跟在寫之後。讀完數據後就廢掉了。

echo a_command | another_command

有時您無法在啟動命令時創建管道。在這些情況下,您可以使用命名管道或 unix-sockets。

使用其中一個程序創建和銷毀一個 unix-socket。這也是兩種方式。

可以獨立於兩個程序創建命名管道。

zsh並且ksh93具有系統呼叫的內置包裝器lseek(),但沒有 bash,因此您需要呼叫一個單獨的實用程序,該實用程序可以對附加到 fdlseek()打開文件描述執行 zsh預設情況下安裝在 macos 上,因此,如果您必須使用bash,您可以定義一個包裝的內置sysseek函式:zsh``sysseek

sysseek() {
 zsh -c '
   zmodload zsh/system
   sysseek "$@" || syserror -p "$0: "
 ' sysseek "$@"
}

並使用:

sysseek -u 3 0

將 fd 3 倒回到開頭。

沒有系統的另一種選擇zsh是使用perl更廣泛可用的系統:

sysseek() { # args: fd, mode, offset
 perl -e '
   my ($fd, $mode, $offset) = @ARGV;
   open FD, ">&", $fd or die "fd $fd: $!\n";
   seek FD, $mode, $offset or die "seek: $!\n";
 ' -- "$@"
}

並使用:

sysseek 3 0 0

將 fd 3 倒回到開頭。

第二個參數可以是:

  1. seek 相對於開始(如sysseek -w startin zsh,預設存在)。
  2. 相對於目前位置 ( zsh’s sysseek -w current)尋找
  3. 相對於文件末尾 ( zsh’s sysseek -w end) 查找。

引用自:https://unix.stackexchange.com/questions/691539