Bash

Bash - 將輸出重定向到變數或文件描述符,然後從變數或文件描述符中讀取

  • February 10, 2016

下面是一個 bash 腳本範例,它將所有輸出重定向到一個文件(並在螢幕上顯示輸出):

# writing to it
exec > >(tee --ignore-interrupts ./log)
exec 2>&1

echo "here is a message"

# reading from it again
cat ./log

現在我不想創建文件./log。我想將所有標準輸出保留在記憶體中,並能夠稍後在腳本中再次讀取它。此外,我處於可能沒有安裝根文件系統的情況。

我嘗試使用程序替換而不是 來做到這一點./log,但我似乎無法理解如何將程序替換創建的文件描述符傳遞給後續命令以讀取我剛剛寫的內容。

這是另一個例子:

# can I make ./log a temporary file descriptor, an in-memory buffer?
exec 3>./log 4<./log
# writes
echo >&3 "hello"
# reads
cat <&4

如果您想全域重定向碰巧編寫的所有內容(就像您現在所做的那樣),這很棘手,但可以一起破解。

我強烈建議,如果可能的話,只需使用普通管道即可。只需將您所做的一切都包裝在一個子shell中。在這種情況下

(
echo "this is the message"
other stuff
) | cat

或者只是使用“$()”語法將所有內容寫入變數。

下一種方法是使用您所做的,但寫入 tmpfs 或/dev/shm如果它們可用。這很簡單,但是您必須知道有哪些基於 ram 的文件系統(如果可能,請進行設置)。

另一種方法是使用mkfifo. 在這兩種情況下,您都需要自己清理。

編輯:

我有一個非常醜陋的 hack,但我敢打賭有人可以改進它。

#!/bin/bash
exec 3>&1
exec > >( tee >( ( tac && echo _ ) | tac | (read && cat > ./log) ) )

echo "lol"
sleep 5
echo "lol"

echo "finished writing"

exec >&-
exec >&3
exec 3>&-
echo "stdout now reopen"
sleep 1 #wait if the file is still being written asynchronously
cat ./log

它是如何工作的:首先,你有一個tee這樣你就可以看到發生了什麼。這又輸出到另一個程序替換。在那裡,您有技巧tac|tac(因為tac需要整個輸入才能開始輸出)等待整個流完成後再繼續。最後一部分是在一個子shell中,它實際上將它輸出到一個文件中。當然,最終的 shell 會在實例化後立即在文件系統中創建輸出文件,如果那是唯一的行的話。因此,必須先完成一些也等待輸入最終到來的事情,以延遲文件創建。我通過首先使用 echo 輸出一個虛擬行,然後讀取並丟棄它來做到這一點。在read您關閉文件描述符之前阻塞,向tac時機已到。因此,最後關閉標準輸出文件描述符。我還在打開程序替換之前保存了原始stdout文件,以便在最後恢復它(再次使用cat)。那裡有一個sleep 5,所以我可以檢查ls該文件是否真的沒有太早創建。最後的睡眠更棘手……子shell是非同步的,如果有很多輸出,你正在等待兩個tacs在文件真正存在之前做他們的事情。所以合理地,你可能需要做其他事情來檢查事情是否真的完成了。例如,&& touch sentinel在最後一個 subshel​​l 的末尾,然後while [ ! -f sentinel ]; do sleep 1; done && rm sentinel在您最終使用該文件之前。

總而言之,兩個程序替換和內部另一個兩個子殼和 2 個管道。這是我寫過的最醜陋的東西之一……但它應該只在您關閉標準輸出時創建文件,這意味著它可以很好地控制並且可以在您的文件系統準備好時完成。

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