ฉันคาดหวัง
find . -delete
เพื่อลบไดเรกทอรีปัจจุบัน แต่ทำไม่ได้ ทำไมไม่
find . -print
แต่มันไม่สอดคล้องกับเช่น
cd ..; rm -r dir
ด้วยเปลือกอื่นที่มีความหมายค่อนข้างชัดเจน ...
ฉันคาดหวัง
find . -delete
เพื่อลบไดเรกทอรีปัจจุบัน แต่ทำไม่ได้ ทำไมไม่
find . -print
แต่มันไม่สอดคล้องกับเช่น
cd ..; rm -r dir
ด้วยเปลือกอื่นที่มีความหมายค่อนข้างชัดเจน ...
คำตอบ:
สมาชิกที่findutils
ตระหนักถึงมันมันเข้ากันได้กับ * BSD:
หนึ่งในเหตุผลที่เราข้ามการลบ "." สำหรับความเข้ากันได้กับ * BSD ซึ่งการกระทำนี้เกิดขึ้น
ข่าวใน findutils แหล่งที่มาแสดงรหัสที่พวกเขาตัดสินใจที่จะเก็บพฤติกรรม:
#20802: If -delete fails, find's exit status will now be non-zero. However, find still skips trying to delete ".".
[อัปเดต]
เนื่องจากคำถามนี้กลายเป็นหนึ่งในหัวข้อยอดนิยมดังนั้นฉันจึงดำดิ่งลงสู่ซอร์สโค้ด FreeBSD และออกมาด้วยเหตุผลที่น่าเชื่อถือมากขึ้น
เรามาดูรหัสแหล่งค้นหายูทิลิตี้ของ FreeBSD :
int
f_delete(PLAN *plan __unused, FTSENT *entry)
{
/* ignore these from fts */
if (strcmp(entry->fts_accpath, ".") == 0 ||
strcmp(entry->fts_accpath, "..") == 0)
return 1;
...
/* rmdir directories, unlink everything else */
if (S_ISDIR(entry->fts_statp->st_mode)) {
if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY)
warn("-delete: rmdir(%s)", entry->fts_path);
} else {
if (unlink(entry->fts_accpath) < 0)
warn("-delete: unlink(%s)", entry->fts_path);
}
...
ในขณะที่คุณสามารถดูถ้ามันไม่ได้กรองและจุดจุดจุดแล้วมันจะถึงrmdir()
ฟังก์ชัน C กำหนดโดย unistd.h
POSIX
ทำการทดสอบอย่างง่าย rmdir พร้อมอาร์กิวเมนต์ dot / dot-dot จะคืนค่า -1:
printf("%d\n", rmdir(".."));
ลองมาดูกันว่า POSIX อธิบาย rmdir อย่างไร :
หากอาร์กิวเมนต์ของพา ธ อ้างถึงพา ธ ที่มีองค์ประกอบสุดท้ายคือ dot หรือ dot-dot rmdir () จะล้มเหลว
shall fail
ไม่มีเหตุผลว่าทำไม
ฉันพบrename
อธิบายบาง reaso n:
ห้ามเปลี่ยนชื่อจุดหรือจุดจุดเพื่อป้องกันเส้นทางของระบบไฟล์วน
เส้นทางระบบไฟล์ที่เป็นวัฏจักร ?
ฉันมองไปที่ภาษาการเขียนโปรแกรม C (รุ่นที่ 2)และค้นหาหัวข้อไดเรกทอรีแปลกใจฉันพบว่ารหัสคล้ายกัน :
if(strcmp(dp->name,".") == 0 || strcmp(dp->name,"..") == 0)
continue;
และแสดงความคิดเห็น!
แต่ละไดเรกทอรีจะมีรายการของตัวเองเสมอที่เรียกว่า "." และพาเรนต์ ".. "; เหล่านี้จะต้องข้ามหรือโปรแกรมประสงค์ห่วงตลอดไป
"วนซ้ำตลอดไป"ซึ่งเหมือนกับวิธีrename
อธิบายว่าเป็น"เส้นทางระบบไฟล์ของวัฏจักร"ด้านบน
ฉันปรับเปลี่ยนรหัสเล็กน้อยและเพื่อให้มันทำงานใน Kali Linux ตามคำตอบนี้ :
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
void fsize(char *);
void dirwalk(char *, void (*fcn)(char *));
int
main(int argc, char **argv) {
if (argc == 1)
fsize(".");
else
while (--argc > 0) {
printf("start\n");
fsize(*++argv);
}
return 0;
}
void fsize(char *name) {
struct stat stbuf;
if (stat(name, &stbuf) == -1 ) {
fprintf(stderr, "fsize: can't access %s\n", name);
return;
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
dirwalk(name, fsize);
printf("%81d %s\n", stbuf.st_size, name);
}
#define MAX_PATH 1024
void dirwalk(char *dir, void (*fcn)(char *))
{
char name[MAX_PATH];
struct dirent *dp;
DIR *dfd;
if ((dfd = opendir(dir)) == NULL) {
fprintf(stderr, "dirwalk: can't open %s\n", dir);
return;
}
while ((dp = readdir(dfd)) != NULL) {
sleep(1);
printf("d_name: S%sG\n", dp->d_name);
if (strcmp(dp->d_name, ".") == 0
|| strcmp(dp->d_name, "..") == 0) {
printf("hole dot\n");
continue;
}
if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name)) {
printf("mocha\n");
fprintf(stderr, "dirwalk: name %s/%s too long\n",
dir, dp->d_name);
}
else {
printf("ice\n");
(*fcn)(dp->d_name);
}
}
closedir(dfd);
}
มาดูกัน:
xb@dnxb:/test/dot$ ls -la
total 8
drwxr-xr-x 2 xiaobai xiaobai 4096 Nov 20 04:14 .
drwxr-xr-x 3 xiaobai xiaobai 4096 Nov 20 04:14 ..
xb@dnxb:/test/dot$
xb@dnxb:/test/dot$ cc /tmp/kr/fsize.c -o /tmp/kr/a.out
xb@dnxb:/test/dot$ /tmp/kr/a.out .
start
d_name: S..G
hole dot
d_name: S.G
hole dot
4096 .
xb@dnxb:/test/dot$
มันทำงานอย่างถูกต้องตอนนี้จะเกิดอะไรขึ้นถ้าฉันแสดงความคิดเห็นcontinue
คำแนะนำ
xb@dnxb:/test/dot$ cc /tmp/kr/fsize.c -o /tmp/kr/a.out
xb@dnxb:/test/dot$ /tmp/kr/a.out .
start
d_name: S..G
hole dot
ice
d_name: S..G
hole dot
ice
d_name: S..G
hole dot
ice
^C
xb@dnxb:/test/dot$
อย่างที่คุณเห็นฉันต้องใช้Ctrl+ Cเพื่อฆ่าโปรแกรมวนลูปไม่สิ้นสุดนี้
ไดเรกทอรี '.. ' อ่านรายการแรก '.. ' และวนซ้ำตลอดไป
สรุป:
GNU findutils
พยายามที่จะเข้ากันได้กับfind
สาธารณูปโภคในBSD *
find
ยูทิลิตี้ใน * BSD ใช้rmdir
ฟังก์ชัน C ที่สอดคล้องกับ POSIX ภายในซึ่งไม่อนุญาตให้ใช้ dot / dot-dot
เหตุผลของการrmdir
ไม่อนุญาตให้ dot / dot-dot คือการป้องกันเส้นทางระบบไฟล์วงจร
ภาษาการเขียนโปรแกรม C ที่เขียนโดย K&R แสดงตัวอย่างว่า dot / dot-dot จะนำไปสู่โปรแกรมวนซ้ำตลอดไปอย่างไร
เพราะfind
คำสั่งของคุณกลับ.
มาเป็นผล จากหน้าข้อมูลของrm
:
ความพยายามที่จะลบไฟล์ที่มีส่วนประกอบของชื่อไฟล์สุดท้ายคือ '.' หรือ '.. ' ถูกปฏิเสธโดยไม่ต้องแจ้งใด ๆ ตามที่กำหนดโดย POSIX
ดังนั้นดูเหมือนว่าfind
จะยึดติดกับกฎ POSIX ในกรณีนี้
/var/log
และคุณเรียกใช้เป็น root คิดว่ามันจะลบส่วนย่อยทั้งหมดและลบไดเรกทอรีปัจจุบันด้วยหรือไม่
man
หน้าสำหรับfind
พูดว่า: "หากการลบล้มเหลวข้อความแสดงข้อผิดพลาดจะปรากฏขึ้น" ทำไมไม่มีข้อผิดพลาดพิมพ์ออกมา?
mkdir foo && cd foo && rmdir $(pwd)
ถอดไดเรกทอรีปัจจุบันทำงานได้ดีโดยทั่วไป: มันกำลังลบ.
(หรือ..
) ที่ใช้งานไม่ได้
เรียกระบบ rmdir ล้มเหลวด้วย EINVAL "."
ถ้าองค์ประกอบสุดท้ายของเส้นทางการโต้แย้งของมันคือ มันเป็นเอกสารที่http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html
และเหตุผลสำหรับพฤติกรรมคือ:
ความหมายของการลบชื่อพา ธ / จุดนั้นไม่ชัดเจนเนื่องจากชื่อไฟล์ (ไดเรกทอรี) ในไดเรกทอรีหลักที่จะลบนั้นไม่ชัดเจนโดยเฉพาะอย่างยิ่งเมื่อมีลิงก์หลายลิงค์ไปยังไดเรกทอรี
การโทรrmdir(".")
ในฐานะการเรียกของระบบไม่ทำงานเมื่อฉันลองใช้ดังนั้นเครื่องมือระดับสูง ๆ จึงไม่สามารถประสบความสำเร็จได้
คุณต้องลบไดเรกทอรีด้วยชื่อจริงไม่ใช่.
นามแฝง
ในขณะที่林果皞และโธมัสให้คำตอบที่ดีกับเรื่องนี้แล้วฉันรู้สึกว่าคำตอบของพวกเขาลืมที่จะอธิบายว่าทำไมพฤติกรรมนี้จึงถูกนำมาใช้ตั้งแต่แรก
ในfind . -delete
ตัวอย่างของคุณการลบไดเรกทอรีปัจจุบันฟังดูมีเหตุผลและมีเหตุผล แต่พิจารณา:
$ find . -name marti\*
./martin
./martin.jpg
[..]
การลบ.
เสียงยังคงมีเหตุผลและมีเหตุผลสำหรับคุณหรือไม่
การลบไดเรกทอรีที่ไม่ว่างเปล่าเป็นข้อผิดพลาด - ดังนั้นคุณไม่น่าจะสูญเสียข้อมูลด้วยfind
(แม้ว่าคุณจะทำได้ด้วยrm -r
) - แต่เชลล์ของคุณจะมีไดเรกทอรีการทำงานปัจจุบันตั้งเป็นไดเรกทอรีที่ไม่มีอยู่อีกต่อไปทำให้เกิดความสับสน และพฤติกรรมที่น่าประหลาดใจ:
$ pwd
/home/martin/test
$ rm -r ../test
$ touch foo
touch: cannot touch 'foo': No such file or directory
การไม่ลบไดเรกทอรีปัจจุบันเป็นการออกแบบอินเตอร์เฟสที่ดีและสอดคล้องกับหลักการของความประหลาดใจน้อยที่สุด