UVA10966 3KP-BASH Project Sol.

  1. 1. 1. cd path
  2. 2. 2. touch filename [-size] [-h]
  3. 3. 3. mkdir path [-h]
  4. 4. 4. find filename [-r] [-h]
  5. 5. 5. ls [path] [-h] [-r] [-s] [-S] [-f] [-d]
  6. 6. 6. pwd
  7. 7. 7. exit
  8. 8. 8. grep “string”

啊…太坑了

我放弃了

为了让后人不再掉坑,我在这里将刘汝佳在《算法竞赛入门经典——训练指南》中的翻译打出来。

(为了加强严密性,题目文字根据官方数据进行了增删)

你的任务是为一个假想的 3KP 操作系统编写一个简单的 Bash 模拟器。

由于操作系统本身还没有写完,你暂时需要在模拟器的内存中虚拟出一个文件系统,而不是和真是的文件系统交互。

下面介绍 3KP 操作系统中的文件系统

文件(file)是数据存储的最小单位。文件名有英文字母(大小写敏感)、数字和点(.)组成,但不能包含两个连续的点,且用户创建的文件名不能是单个的点字符(即 .

文件名长度不能超过 ,文件大小不能超过 。一个文件可能具有 directory 属性和 hidden 属性

目录(directory) 是一个大小为 ,具有 directory 属性的文件,里面可以保存任意数量的文件(无论属性)。

空文件系统只有一个文件,叫做“根目录”。在任意时刻,有一个称为“当前目录”的目录。Bash 启动时,当前目录就是根目录。

指令中引用文件的时候,可以使用绝对路径和相对路径

绝对路径以字符“/”开头,如“/home/acm/uva”;相对路径不以字符“/”,当前目录和父目录分别用“.”和“…”表示。

如当前目录为“/home/acm/uva”,那么“…/…/…/home/…”就是根目录。

这里展示访问的过程:

1
2
3
4
5
6
/home/acm/uva  初始目录
/home/acm ../
/home ../
/ ../
/home home/
/ ..

你的 Bash 模拟器需要支持 个命令,具体如下:

(没有 [] 标志的参数为必选参数)

(又:Markdown 表格不支持回车,只能直接打了 qwq)

1. cd path

改变当前目录。path 可以是相对路径,也可以是绝对路径。

如果目录不存在或名字不合法,输出 path not found

2. touch filename [-size] [-h]

修改文件。

filename 应由两部分组成,前一部分是目录(为空就是当前目录),后一部分为文件名。

比如:

1
2
3
/fc.cpp        -> 目录:/           文件名: fc.cpp
/home/fc.cpp -> 目录:/home 文件名: fc.cpp
qwq.cpp -> 目录:(当前目录) 文件名: qwq.cpp

都是合法的。

其中的文件名应当是合法的文件名,否则输出 bad usage(见后)。其中的目录应当是当前文件系统中存在的目录,否则输出 path not found

在正常情况下,同名文件(如果有的话),应当先被删除,然后新建文件。

文件的大小由 -size 参数决定(若没有参数 -size 默认为 0),参数 -h 表示创建隐藏文件。用户不会制定多个 -size 参数。

如果存在一个同名的目录,输出 a directory with the same name exists

3. mkdir path [-h]

与 touch 类似。

创建目录。

path 应由两部分组成,前一部分是目录(为空就是当前目录),后一部分为新建目录名。

其中要新建的目录名应当是合法的目录名,否则输出 bad usage。其中的目录应当是当前文件系统中存在的目录,否则输出 path not found

参数 -h 表示创建隐藏目录。

如果存在同名目录或文件,输出 file or directory with the same name exists

4. find filename [-r] [-h]

查找目录或文件。

filename 应由两部分组成,前一部分是目录(为空就是当前目录),后一部分表示要查找的文件名(注意,这里表示在前一部分目录中寻找后一部分中的文件)。

前一部分目录应当是当前文件系统中存在的目录,否则输出 path not found

-r 参数表示要查找的目录下的所有子目录都要查找。默认情况下,find 命令不会显示隐藏文件,(但是在有 -r 参数的情况下是会搜索隐藏子目录的),如果指定了 -h 参数,则会显示所有搜索到的隐藏文件。

如果没有找到需要输出的文件,则输出 file not found。否则对于找到的每个文件,输出单独的一行,按顺序会这样输出:

1
filepath/filename size [hidden] [dir]

其中如果结果是隐藏或目录文件,则依次输出 hidden 和 dir。中间要有空格隔开。

结果应当按每个文件的绝对路径的字典序从小到大排序。

5. ls [path] [-h] [-r] [-s] [-S] [-f] [-d]

展示目录中文件。

总共 个可选参数,没有必选参数。

ls 展示方式与 find 相同。

path 指定展示的目录。若没有 path 选项,则制定当前目录为要展示的目录。

-h,-r 与 find 指令相同。

-s,-S 表示输出的顺序,-s 表示按照文件大小从小到大排序,-S 表示按照文件大小从大到小排序。如果没有制定其中任意一个参数,那么排序方式与 find 一样。又如果,两文件大小相同,那么也按照 find 的比较方式来排序。

比如说,如果没有指定 -s 和 -S 参数的结果是这样的:

1
2
3
4
/0/a.cpp 4283
/1/a.cpp 4589859858
/2.cpp 0
/a/a.cpp 4283

那么指定了 -s 的参数排序如下:

1
2
3
4
/2.cpp 0
/0/a.cpp 4283
/a/a.cpp 4283
/1/a.cpp 4589859858

指定了 -S 的参数排序如下:

1
2
3
4
/1/a.cpp 4589859858
/0/a.cpp 4283
/a/a.cpp 4283
/2.cpp 0

-f 表示只显示没有 directory 属性的文件;

-d 表示只显示有 directory 属性的文件。

注意:两个参数可以同时出现。

6. pwd

输出当前目录的绝对路径。

7. exit

退出 Bash。

在本题中,表示初始化 Bash。

8. grep “string”

筛出输入字符串中含有 string 串的行。

在本题中,grep 只能通过管道调用。

何为管道?

简单理解,就是将管道分隔符(|前面的程序输出结果输入到后面的程序中。

比如说,假设 ls 命令当前是能够输出这些内容的:

1
2
3
4
/1 5
/5 6
/6 7
/3 5 hidden

如果我的命令是这样子的:ls | grep "5"

那么,grep 会筛取前面输入的字符串,并筛去不含 “5” 的行。

加上 grep 结果如下:

1
2
3
/1 5
/5 6
/3 5 hidden

注意:管道可以叠加使用,只检查 string(不带有双引号)。

对于上面的结果,我们其实可以再添加一层 grep。

命令变为 ls | grep "5" | grep " 5"(注意后面的 grep 中有空格),那么输出就变成了:

1
2
/1 5
/3 5 hidden

由于在前一次 grep 结果中,第二行找不到 " 5",所以,第二行被筛去了。

命令行中每一行包含一条或多条命令,用管道分隔符隔开(管道分隔符前后不一定有空格)。

第一条命令必须是除了 grep 之外的上述命令之一,而剩下的命令必须是 grep。

如果违反上述(上两行)中的规定,应输出 bad usage,并且不执行任何之后的命令(即之后的 grep)。

比如说:

1
2
3
4
ls | grep "s"|grep "s2"  合法
ls | ls | grep "ss" 不合法,后面的 grep 不执行,直
接输出 bad usage
grep | ls 不合法

输入保证满足以下条件:

  1. 每个命令的必选参数(如果有的话)都是第一个参数,可选参数在必选参数之后,但可能以任意顺序出现。

  2. 命令和参数,参数和参数之间保证有一个或多个空格。

  3. 用户不会忽略必选参数,不会使用非法参数,也不会在除了 grep 外的其他命令中使用引号。

如果第一条命令是 touch 或者 mkdir,且命令返回 bad usage,则不执行后面的命令(即 grep)。

模拟思路:

  1. 拆分命令和参数。

  2. 用一棵树来表示文件系统。

  3. 注意以格式化输出。

这里贴心的附上刘汝佳的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
// UVa10966 3KP-Bash Project
// Rujia Liu
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<sstream>
#include<cstring>
using namespace std;

typedef unsigned long long LL;
typedef vector<int> VI;
typedef vector<string> VS;

const string ERROR_BAD_USAGE = "bad usage\n";
const string ERROR_NO_COMMAND = "no such command\n";
const string ERROR_DIR_NOT_FOUND = "path not found\n";
const string ERROR_DIR_FOUND = "a directory with the same name exists\n";
const string ERROR_DIR_OR_FILE_FOUND = "file or directory with the same name exists\n";
const string ERROR_FILE_NOT_FOUND = "file not found\n";
const string ERROR_EMPTY = "[empty]\n";

// if delim != ' ', make sure that no space characters exist in s
VS split(string s, char delim=' ') {
if(delim != ' ')
for(int i = 0; i < s.length(); i++) if(s[i] == delim) s[i] = ' ';
stringstream ss(s);
VS ret;
string x;
while(ss >> x) ret.push_back(x);
return ret;
}

// return 1 if success, 0 if on error
int get_int(string s, LL& v) {
stringstream ss(s);
if(ss >> v) return 1;
return 0;
}

string trim(string s) {
int L, R;
for(L = 0; L < s.length(); L++) if(!isspace(s[L])) break;
for(R = s.length()-1; R > L; R--) if(!isspace(s[R])) break;
return s.substr(L, R-L+1);
}

struct File {
int parent;
string name;
string fullpath; // cached fullpath name
LL size;
bool dir;
bool hidden;
vector<int> subdir;
File(int parent=0, string name="", LL size=0, bool dir=true, bool hidden=false):parent(parent),name(name),size(size),dir(dir),hidden(hidden) {}
};

vector<File> fs;
int curDir;

// lexicograhically smaller
bool comp(const int& x, const int& y) {
return fs[x].fullpath < fs[y].fullpath;
}

// compare size first (increasing)
bool comps(const int& x, const int& y) {
return fs[x].size < fs[y].size || (fs[x].size == fs[y].size && fs[x].fullpath < fs[y].fullpath);
}

// compare size first (decreasing)
bool compS(const int& x, const int& y) {
return fs[x].size > fs[y].size || (fs[x].size == fs[y].size && fs[x].fullpath < fs[y].fullpath);
}

// return the node number. -1 on error
int findFileInDirectory(int node, string name) {
VI& subdir = fs[node].subdir;
for(int i = 0; i < subdir.size(); i++)
if(fs[subdir[i]].name == name) return subdir[i];
return -1;
}

// if the end of path is '/', remove it first
string joinPath(string path, string name) {
if(path[path.size()-1] != '/') path += "/";
return path + name;
}

// get the absolute path string for a node
string getAbsolutePath(int node) {
if(!node) return "/";
return joinPath(getAbsolutePath(fs[node].parent), fs[node].name);
}

// create a file (regular/directory) in a node (the caller should ensure that the node represents a directory)
int createFileInDirectory(int node, string name, LL size, bool dir, bool hidden) {
fs.push_back(File(node, name, size, dir, hidden));
int x = fs.size()-1;
fs[x].fullpath = joinPath(getAbsolutePath(node), name);
fs[node].subdir.push_back(x);
return x;
}

// return the node of a path (could be relative path or absolute path). return -1 on error
int getDirNode(string path) {
if(!path.length()) return curDir;
int node = curDir; // relative path by default
if(path[0] == '/') node = 0; // absolute path
VS dirs = split(path, '/');
for(int i = 0; i < dirs.size(); i++) {
if(dirs[i] == ".") continue;
else if(dirs[i] == "..") {
if(!node) return -1; // root has no parent
node = fs[node].parent;
} else {
int x = findFileInDirectory(node, dirs[i]);
if(x == -1 || !fs[x].dir) return -1; // cannot enter a regular file
node = x;
}
}
return node;
}

// check if the filename is valid
int isValidFileName(string name) {
if(name.length() == 0 || name.length() > 255) return 0;
if(name == "." || name.find("..") != string::npos) return 0;
for(int i = 0; i < name.length(); i++)
if(!isdigit(name[i]) && !isalpha(name[i]) && name[i] != '.') return 0;
return 1;
}

// split a fullpath into a directory part and a filename part
// return the node of the directory part. return -1 if path not found
int splitFileName(string fullpath, string& filename) {
int n = fullpath.length();
int x = n;
for(int i = fullpath.length()-1; i >= 0; i--) {
if(fullpath[i] == '/') { // the last '/'
filename = fullpath.substr(i+1);
string dir = fullpath.substr(0, i);
if(dir == "") return 0; // relative to root, NOT current directory
return getDirNode(dir);
}
}
filename = fullpath;
return curDir;
}

// new BASH session
void newSession() { fs.clear(); fs.push_back(File()); curDir = 0; }

// Given the filename, find all matching files in a node, possibly recursively. Append the items in "out", which is a vector of nodes
// if filename == "", we simply list the files
void findFileEx(VI& out, int node, string filename, bool recur, bool hidden, bool f=true, bool d=true) {
VI& subdir = fs[node].subdir;
for(int i = 0; i < subdir.size(); i++) {
int x = subdir[i];
if(fs[x].dir && recur) findFileEx(out, x, filename, recur, hidden, f, d);
if(fs[x].hidden && !hidden) continue;
if(filename == "" || fs[x].name == filename) {
if((fs[x].dir && d) || (!fs[x].dir && f)) out.push_back(x);
}
}
}

// format the file list
string formatFiles(const VI& out) {
stringstream ss;
for(int i = 0; i < out.size(); i++) {
ss << fs[out[i]].fullpath << " " << fs[out[i]].size;
if(fs[out[i]].hidden) ss << " " << "hidden";
if(fs[out[i]].dir) ss << " " << "dir";
ss << "\n";
}
return ss.str();
}

// get arguments and switches
bool parseArgs(VS params, VS& args, bool* sw, LL &v) {
LL v2;
for(int i = 0; i < params.size(); i++)
if(params[i][0] == '-') {
if(isalpha(params[i][1])) sw[params[i][1]] = 1;
else if(get_int(params[i].substr(1), v2)) v = v2; // if there are more than one, only the last one is used
else return false;
}
else args.push_back(params[i]); // regular arguments (without prefix '-')
return true;
}

// run a command (except grep) without piping, returns the standard output
string runCommand(const VS& cmd) {
VS params(cmd.begin()+1, cmd.end()), args;
bool sw[256];
LL v = 0;
memset(sw, 0, sizeof(sw));
if(!parseArgs(params, args, sw, v)) return ERROR_BAD_USAGE;

int node;
string filename;
if(cmd[0] == "cd") {
if(args.size() != 1) return ERROR_BAD_USAGE;
if((node = getDirNode(args[0])) == -1) return ERROR_DIR_NOT_FOUND;
curDir = node;
return "";
} else if(cmd[0] == "touch") {
if(args.size() != 1) return ERROR_BAD_USAGE;
if((node = splitFileName(args[0], filename)) == -1) return ERROR_DIR_NOT_FOUND;
if(!isValidFileName(filename)) return ERROR_BAD_USAGE; //!

int x = findFileInDirectory(node, filename);
if(x != -1 && fs[x].dir) return ERROR_DIR_FOUND;
if(x == -1) createFileInDirectory(node, filename, v, false, sw['h']);
else { fs[x].size = v; fs[x].hidden = sw['h']; }
return "";
}
if(cmd[0] == "mkdir") {
if(args.size() != 1) return ERROR_BAD_USAGE;
if((node = splitFileName(args[0], filename)) == -1) return ERROR_DIR_NOT_FOUND;
if(!isValidFileName(filename)) return ERROR_BAD_USAGE; //!

int x = findFileInDirectory(node, filename);
if(x != -1) return ERROR_DIR_OR_FILE_FOUND;
createFileInDirectory(node, filename, 0, true, sw['h']);
return "";
}
if(cmd[0] == "find") {
if(args.size() != 1) return ERROR_BAD_USAGE;
if((node = splitFileName(args[0], filename)) == -1) return ERROR_DIR_NOT_FOUND;

VI out;
findFileEx(out, node, filename, sw['r'], sw['h']);
if(out.size() == 0) return ERROR_FILE_NOT_FOUND;
sort(out.begin(), out.end(), comp);
return formatFiles(out);
}
if(cmd[0] == "ls") {
if(args.size() > 1) return ERROR_BAD_USAGE;
node = curDir;
if(args.size() == 1) if((node = getDirNode(args[0])) == -1) return ERROR_DIR_NOT_FOUND;

VI out;
findFileEx(out, node, "", sw['r'], sw['h'], !sw['d'], !sw['f']); // ignore bad usage "-d -f"
if(out.size() == 0) return ERROR_EMPTY;
if(sw['s']) sort(out.begin(), out.end(), comps);
else if(sw['S']) sort(out.begin(), out.end(), compS); // ignore bad usage "-s -S"
else sort(out.begin(), out.end(), comp);
return formatFiles(out);
}
if(cmd[0] == "pwd") {
if(args.size() != 0) return ERROR_BAD_USAGE;
return getAbsolutePath(curDir) + "\n";
}
if(cmd[0] == "exit") {
if(args.size() != 0) return ERROR_BAD_USAGE;
newSession();
return "";
}
if(cmd[0] == "grep") return ERROR_BAD_USAGE;
return ERROR_NO_COMMAND;
}

// run a commandline (pipes are possible)
string runCommandLine(string cmd) {
// split commands
int n = cmd.length();
int start = 0, inq = 0;
VS commands;
for(int i = 0; i <= n; i++)
if(i == n || (cmd[i] == '|' && !inq)) { commands.push_back(cmd.substr(start, i-start)); start = i+1; }
else if(cmd[i] == '"') inq = !inq;
if(!commands.size()) return "";

// run the first command
string lastoutput = runCommand(split(commands[0]));
string line, s, ret;
// chain the greps after that
for(int i = 1; i < commands.size(); i++) {
stringstream ss(commands[i]);
if(!(ss >> s) || s != "grep") return ERROR_BAD_USAGE;
getline(ss, s);
s = trim(s);
if(s.length() < 2 || s[0] != '"' || s[s.length()-1] != '"') return ERROR_BAD_USAGE;
s = s.substr(1, s.length()-2); // get the string to be searched for

stringstream input(lastoutput);
ret = "";
while(getline(input, line)) {
if(line.find(s) != string::npos) ret += line + "\n";
}
lastoutput = ret;
}
return lastoutput;
}

int main() {
string cmd;
newSession();
while(getline(cin, cmd)) {
cout << runCommandLine(cmd);
}
return 0;
}

我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define VI vector<int>
#define VS vector<string>
#define strs stringstream
const string ERROR_BAD_USAGE = "bad usage\n";
const string ERROR_NO_COMMAND = "no such command\n";
const string ERROR_DIR_NOT_FOUND = "path not found\n";
const string ERROR_DIR_FOUND = "a directory with the same name exists\n";
const string ERROR_DIR_OR_FILE_FOUND = "file or directory with the same name exists\n";
const string ERROR_FILE_NOT_FOUND = "file not found\n";
const string ERROR_EMPTY = "[empty]\n";

struct file{
string fname;
ll fsize;
bool is_dir,is_hidden;
string fullpath;
int father;
VI subs;
file(string fn,ll size,bool dir,bool hidden,int fa): fname(fn),fsize(size),is_dir(dir),is_hidden(hidden),father(fa)
{
subs.clear();
}
};
vector<file> fs;
int np;

void init_else(file &f)
{
if(f.father>=fs.size()) f.fullpath="w?";
if(f.father==-1) f.fullpath="/";
else if(f.father==0) f.fullpath="/"+f.fname;
else f.fullpath=fs[f.father].fullpath+"/"+f.fname;
}

void init()
{
fs.clear();
file f("",0,true,false,-1);
init_else(f);
fs.push_back(f);
np=0;
}

VS split_pipe(string cmd_aline)
{
VS v;v.clear();
int flag=1,last=0;
for(int i=0;i<=cmd_aline.length();i++)
if(i==cmd_aline.length()||flag&&cmd_aline[i]=='|')
v.push_back(cmd_aline.substr(last,(i-last))),last=i+1;
else if(cmd_aline[i]=='\"')
flag^=1;
return v;
}

void trim(string& s) // 去空格
{
int l=0,r=s.length()-1;
while(s[l]==' ') l++;
while(s[r]==' ') r--;
s=s.substr(l,(r-l+1)); // 同时去除了两遍的双引号
}

bool tf[200];

VS split_by_space(string cmd)
{
VS v; string s; strs ss(cmd);
while(ss>>s) v.push_back(s);
return v;
}

bool to_ll(string s,ll& v)
{
strs sin(s);
if(sin>>v) return true;
return false;
}

bool split_args(string &cmd,VS &must,ll &size)
{
memset(tf,0,sizeof(tf));
must.clear();
size=0;
VS cmds=split_by_space(cmd);
ll v;
// if(cmds.size()==0) return true;
cmd=cmds[0];
for(int i=1;i<cmds.size();i++)
if(cmds[i][0]=='-')
{
if(cmds[i].length()<2) return true;
if(isalpha(cmds[i][1]))
{
if(tf[cmds[i][1]]) return true;
tf[cmds[i][1]]=true;
}
else if(to_ll(cmds[i].substr(1),v))
{
if(size!=0) return true;
size=v;
}
else return true;
}
else must.push_back(cmds[i]);

return false;
}

VS split(string s,char ch)
{
VS v;
string ns="";
for(int i=0;i<s.length();i++)
if(s[i]==ch) v.push_back(ns),ns="";
else ns=ns+s[i];

if(ns!="") v.push_back(ns);
return v;
}

int find_son(int p,string str)
{
if(p<0||p>=fs.size()) return -1;
for(int i=0;i<fs[p].subs.size();i++)
if(fs[fs[p].subs[i]].fname==str)
return fs[p].subs[i];
return -1;
}

int find_dir(string dir)
{
if(dir=="") return 0;
if(dir=="/") return 0;

int nq=np;
if(dir[0]=='/') nq=0,dir=dir.substr(1);
VS dirs=split(dir,'/');
for(int i=0;i<dirs.size();i++)
{
if(dirs[i]=="") continue;
if(dirs[i]==".") continue;
if(dirs[i]=="..")
{
if(nq==0) return -1;
else nq=fs[nq].father;
continue;
}
nq=find_son(nq,dirs[i]);
if(nq==-1||!fs[nq].is_dir) return -1;
}
return nq;
}

void split_fp(int& path,string& fname)
{
size_t pt=fname.rfind("/");
if(pt==fname.npos) path=np;
else
{
path=find_dir(fname.substr(0,pt));
fname=fname.substr(pt+1);
}
}

bool fn_is_valid(string fname)
{
if(fname.length()==0||fname.length()>255) return true;
if(fname=="."||fname.find("..")!=fname.npos) return true;
for(int i=0;i<fname.length();i++)
if(!isdigit(fname[i])&&!isalpha(fname[i])&&fname[i]!='.') return true;

return false;
}

int check_file_name(string filename,int father) //-1:no x:file -2:dir
{
if(father<0||father>=fs.size()) return -1;
for(int i=0;i<fs[father].subs.size();i++)
if(fs[fs[father].subs[i]].fname==filename)
{
if(fs[fs[father].subs[i]].is_dir) return -2;
else return fs[father].subs[i];
}
return -1;
}

string make_file(string filename,ll size,int father,bool is_hidden)
{
int ret=check_file_name(filename,father);
if(ret==-2) return ERROR_DIR_FOUND;

if(ret==-1)
{
file f(filename,size,false,is_hidden,father);
init_else(f);
fs.push_back(f);
fs[father].subs.push_back(fs.size()-1);
}
else
{
fs[ret].fsize=size;
fs[ret].is_hidden=is_hidden;
}
return "";
}

string make_dir(string filename,int father,bool is_hidden)
{
if(check_file_name(filename,father)!=-1) return ERROR_DIR_OR_FILE_FOUND;

file f(filename,0,true,is_hidden,father);
init_else(f);
fs.push_back(f);
fs[father].subs.push_back(fs.size()-1);
return "";
}

void find(int path,string filename,VI &ans,bool r)
{
for(int i=0;i<fs[path].subs.size();i++)
{
file& f=fs[fs[path].subs[i]];
if(filename==""||f.fname==filename) ans.push_back(fs[path].subs[i]);
if(r&&f.is_dir) find(fs[path].subs[i],filename,ans,r);
}
}

bool cmp(int a,int b)
{
return fs[a].fullpath<fs[b].fullpath;
}

bool cmp_s(int a,int b)
{
if(fs[a].fsize!=fs[b].fsize) return fs[a].fsize<fs[b].fsize;
return fs[a].fullpath<fs[b].fullpath;
}

bool cmp_S(int a,int b)
{
if(fs[a].fsize!=fs[b].fsize) return fs[a].fsize>fs[b].fsize;
return fs[a].fullpath<fs[b].fullpath;
}

string run_cmd(string cmd)
{
if(np<0||np>=fs.size()) return ERROR_BAD_USAGE;
ll size=0;
VS must;
if(split_args(cmd,must,size)) return ERROR_BAD_USAGE;
if(cmd=="cd")
{
if(must.size()!=1) return ERROR_BAD_USAGE;
int nd=find_dir(must[0]);
if(nd==-1) return ERROR_DIR_NOT_FOUND;

np=nd;
return "";
}
if(cmd=="mkdir")
{
if(must.size()!=1) return ERROR_BAD_USAGE;
int path;string fn=must[0];

split_fp(path,fn);
if(path==-1) return ERROR_DIR_NOT_FOUND;
if(fn_is_valid(fn)) return ERROR_BAD_USAGE;
return make_dir(fn,path,tf['h']);
}
if(cmd=="touch")
{
if(must.size()!=1) return ERROR_BAD_USAGE;
int path;string fn=must[0];

split_fp(path,fn);
if(path==-1) return ERROR_DIR_NOT_FOUND;
if(fn_is_valid(fn)) return ERROR_BAD_USAGE;
return make_file(fn,size,path,tf['h']);
}
if(cmd=="find")
{
VI ans;
if(must.size()!=1) return ERROR_BAD_USAGE;
int path;string fn=must[0];
split_fp(path,fn);
if(path==-1) return ERROR_DIR_NOT_FOUND;
find(path,fn,ans,tf['r']);

sort(ans.begin(),ans.end(),cmp);

strs ss;
for(int i=0;i<ans.size();i++)
if(tf['h']||!fs[ans[i]].is_hidden)
{
ss<<fs[ans[i]].fullpath<<" "<<fs[ans[i]].fsize;
if(fs[ans[i]].is_hidden) ss<<" hidden";
if(fs[ans[i]].is_dir) ss<<" dir";
ss<<endl;
}
string res=ss.str();
if(res=="") res=ERROR_FILE_NOT_FOUND;
return res;
}
if(cmd=="ls")
{
if(must.size()>1) return ERROR_BAD_USAGE;
// if(tf['d']&&tf['f']) return "[empty]\n";
VI ans;
int path=np;
if(must.size()!=0) path=find_dir(must[0]);
if(path==-1) return ERROR_DIR_NOT_FOUND;
find(path,"",ans,tf['r']);
// cout<<ans.size()<<endl;

if(tf['s']) sort(ans.begin(),ans.end(),cmp_s);
else if(tf['S']) sort(ans.begin(),ans.end(),cmp_S);
else sort(ans.begin(),ans.end(),cmp);

strs ss;
for(int i=0;i<ans.size();i++)
if((tf['h']||!fs[ans[i]].is_hidden) && ( (!tf['d'] && !tf['f']) || (tf['d'] && fs[ans[i]].is_dir) || (tf['f'] && !fs[ans[i]].is_dir)))
{
ss<<fs[ans[i]].fullpath<<" "<<fs[ans[i]].fsize;
if(fs[ans[i]].is_hidden) ss<<" hidden";
if(fs[ans[i]].is_dir) ss<<" dir";
ss<<endl;
}
string res=ss.str();
if(res=="") res=ERROR_EMPTY;
return res;
}
if(cmd=="pwd")
{
if(must.size()!=0) return ERROR_BAD_USAGE;
return fs[np].fullpath+"\n";
}
if(cmd=="exit")
{
if(must.size()!=0) return ERROR_BAD_USAGE;
init();
return "";
}
if(cmd=="grep") return ERROR_BAD_USAGE;
return ERROR_NO_COMMAND;
}

string run_aline(string cmd_aline)
{
VS cmd_pipe=split_pipe(cmd_aline);
string result=run_cmd(cmd_pipe[0]),s,gl;
// if(result==BAD_USAGE_ERROR) return BAD_USAGE_ERROR;
bool flag=false;
// if(result==NO_SUCH_COMMAND_ERROR) flag=true;
for(int i=1;i<cmd_pipe.size();i++)
{
strs ss(cmd_pipe[i]);
if(!(ss>>s)||s!="grep") return ERROR_BAD_USAGE;
getline(ss,s);
trim(s);
if(s.length()<2||s[0]!='\"'||s[s.length()-1]!='\"') return ERROR_BAD_USAGE;
s=s.substr(1,s.length()-2);

strs ss_res(result);
strs ss_out("");
while(getline(ss_res,gl))
if(gl.find(s)!=gl.npos)
ss_out<<gl<<endl;
result=ss_out.str();
}
// if(flag) return NO_SUCH_COMMAND_ERROR;
return result;
}

int main()
{
init();
string cmd;
while(getline(cin,cmd))
{
if(cmd!="") cout<<run_aline(cmd);
}
return 0;
}