2010-10-01 46 views
7

的我試圖用讀/寫文件描述符在bash,這樣我可以刪除該文件描述稱這個文件後來,因爲這樣的:猛砸讀/寫文件的描述 - 尋求啓動文件

F=$(mktemp) 
exec 3<> "$F" 
rm -f "$F" 

echo "Hello world" >&3 
cat <&3 

cat命令沒有輸出。我能達到我想要的,如果我使用的讀取和寫入單獨的文件中描述:

F=$(mktemp) 
exec 3> "$F" 
exec 4< "$F" 
rm -f "$F" 

echo "Hello world" >&3 
cat <&4 

它打印Hello world

我懷疑,當你從寫切換到讀它,下面bash和Python代碼的組合,印證了這一慶典不會自動尋求文件描述符的開始:

fdrw.sh

exec 3<> tmp 
rm tmp 

echo "Hello world" >&3 
exec python fdrw.py 

fdrw.py

import os 

f = os.fdopen(3) 
print f.tell() 
print f.read() 

這給:

$ bash fdrw.sh 
12 

$ # This is the prompt reappearing 

有沒有一種方法可以實現我想使用bash?

+0

爲什麼要在讀/寫之前刪除文件? – unhammer 2011-03-21 10:20:21

+4

在Unix中,當您刪除文件時,該文件實際上並未被刪除,直到所有打開的文件描述符都被關閉。因此,打開後立即刪除一個臨時文件是很常見的做法,因爲它保證沒有其他進程可以惡意更改文件,並且在進程關閉文件或退出後文件已關閉。 – telotortium 2011-03-21 20:16:17

+0

爲什麼你不喜歡你自己擁有分離讀寫描述符的方法?這似乎是最簡單的方法。 – Kelvin 2012-05-14 16:37:45

回答

2

編號bash沒有任何關於「重定向」的概念。它從頭至尾讀取/寫入(大部分)在一個長流中。

+2

那麼,基本上,在bash中讀/寫描述符的唯一原因是將它們傳遞給exec'ed進程? – telotortium 2010-10-01 10:59:02

+1

爲了提供比stdin,stdout和sterr更多的渠道,是的。 – 2010-10-01 11:37:17

3

嘗試改變的命令序列:

F=$(mktemp tmp.XXXXXX) 
exec 3<> "$F" 
echo "Hello world" > "$F" 
rm -f "$F" 

#echo "Hello world" >&3 
cat <&3 
+0

因此寫入文件,然後*刪除*,然後*從它讀取? – 2010-10-01 14:14:11

+5

@Dennis這個解決方案實際上起作用。貓沒有從明顯刪除的文件中讀取。它從仍然打開的描述符中讀取數據。即使最後一個(硬鏈接)鏈接已被刪除,您仍然可以使用該描述符訪問文件的內容。 – Kelvin 2012-05-14 16:44:32

7

如果你碰巧要尋求bash的文件描述符,你可以使用一個子進程,因爲它繼承了父進程的文件描述符。這是一個C程序的例子來做到這一點。

seekfd.c

#define _FILE_OFFSET_BITS 64 
#include <string.h> 
#include <errno.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <unistd.h> 

int main(int argc, char* argv[]) 
{ 
    /* Arguments: fd [offset [whence]] 
    * where 
    * fd: file descriptor to seek 
    * offset: number of bytes from position specified in whence 
    * whence: one of 
    * SEEK_SET (==0): from start of file 
    * SEEK_CUR (==1): from current position 
    * SEEK_END (==2): from end of file 
    */ 
    int fd; 
    long long scan_offset = 0; 
    off_t offset = 0; 
    int whence = SEEK_SET; 
    int errsv; int rv; 
    if (argc == 1) { 
     fprintf(stderr, "usage: seekfd fd [offset [whence]]\n"); 
     exit(1); 
    } 
    if (argc >= 2) { 
     if (sscanf(argv[1], "%d", &fd) == EOF) { 
      errsv = errno; 
      fprintf(stderr, "%s: %s\n", argv[0], strerror(errsv)); 
      exit(1); 
     } 
    } 
    if (argc >= 3) { 
     rv = sscanf(argv[2], "%lld", &scan_offset); 
     if (rv == EOF) { 
      errsv = errno; 
      fprintf(stderr, "%s: %s\n", argv[0], strerror(errsv)); 
      exit(1); 
     } 
     offset = (off_t) scan_offset; 
    } 
    if (argc >= 4) { 
     if (sscanf(argv[3], "%d", &whence) == EOF) { 
      errsv = errno; 
      fprintf(stderr, "%s: %s\n", argv[0], strerror(errsv)); 
      exit(1); 
     } 
    } 

    if (lseek(fd, offset, whence) == (off_t) -1) { 
     errsv = errno; 
     fprintf(stderr, "%s: %s\n", argv[0], strerror(errsv)); 
     exit(2); 
    } 

    return 0; 
} 
5

我找到了一種方法做在bash,但它依靠exec < /dev/stdin一個不起眼的功能,實際上可以根據http://www.madisonlinux.org/pipermail/madlug/2006-February/012896.html倒帶標準輸入的文件描述符:

F=$(mktemp) 
exec 3<> "$F" 
rm -f "$F" 

echo "Hello world" >&3 
{ exec < /dev/stdin; cat; } <&3 

寫描述符不受此影響,因此您仍然可以在cat之前將輸出附加到描述符3。

不幸的是,我只在Linux下不是在MacOS(BSD)下工作,即使是最新的bash版本。所以它看起來不是很便攜。

3

當你用bash打開一個文件描述符時,它可以作爲/dev/fd/中的文件訪問。 在這一點上,你可以做cat,它會從頭開始閱讀,或追加(echo "something" >> /dev/fd/3),並將它添加到最後。 至少在我的系統上,它的行爲如此。 (另一方面,即使我沒有寫任何描述符,我似乎也無法獲得「cat < & 3」的工作。

0
#!/bin/bash 
F=$(mktemp tmp.XXXXXX) 
exec 3<> $F 
rm $F 

echo "Hello world" >&3 
cat /dev/fd/3 

在對方的回答As suggestedcat會從中讀,因爲它認爲它只是一個普通的文件之前,快退給你的文件描述符。

0

要「快退」的文件描述符,你可以簡單地使用/proc/self/fd/3

測試腳本:

#!/bin/bash 

# Fill data 
FILE=test 
date +%FT%T >$FILE 

# Open the file descriptor and delete the file 
exec 5<>$FILE 
rm -rf $FILE 

# Check state of the file 
# should return an error as the file has been deleted 
file $FILE 

# Check that you still can do multiple reads or additions 
for i in {0..5}; do 
    echo ----- $i ----- 

    echo . >>/proc/self/fd/5 
    cat /proc/self/fd/5 

    echo 
    sleep 1 
done 

試圖殺死-9在運行時的腳本,你會看到相反的是陷阱方法發生時,該文件實際上被刪除。